diff --git a/packages/PackageInstaller/.gitignore b/packages/PackageInstaller/.gitignore
new file mode 100644
index 0000000000000..7866e9eb7797c
--- /dev/null
+++ b/packages/PackageInstaller/.gitignore
@@ -0,0 +1,6 @@
+bin/
+gen/
+*.iml
+.project
+.classpath
+project.properties
diff --git a/packages/PackageInstaller/Android.mk b/packages/PackageInstaller/Android.mk
new file mode 100644
index 0000000000000..65b82557bcb6f
--- /dev/null
+++ b/packages/PackageInstaller/Android.mk
@@ -0,0 +1,50 @@
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_USE_AAPT2 := true
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ $(call all-java-files-under, src)
+
+LOCAL_STATIC_ANDROID_LIBRARIES += \
+ androidx.car_car \
+ androidx.design_design \
+ androidx.transition_transition \
+ androidx.core_core \
+ androidx.media_media \
+ androidx.legacy_legacy-support-core-utils \
+ androidx.legacy_legacy-support-core-ui \
+ androidx.fragment_fragment \
+ androidx.appcompat_appcompat \
+ androidx.preference_preference \
+ androidx.recyclerview_recyclerview \
+ androidx.legacy_legacy-preference-v14 \
+ androidx.leanback_leanback \
+ androidx.leanback_leanback-preference \
+ SettingsLib
+
+LOCAL_STATIC_JAVA_LIBRARIES := \
+ xz-java \
+ androidx.annotation_annotation
+
+LOCAL_PACKAGE_NAME := PackageInstaller
+LOCAL_CERTIFICATE := platform
+
+LOCAL_PRIVILEGED_MODULE := true
+
+LOCAL_PROGUARD_FLAG_FILES := proguard.flags
+
+# Comment for now unitl all private API dependencies are removed
+# LOCAL_SDK_VERSION := system_current
+LOCAL_PRIVATE_PLATFORM_APIS := true
+
+include $(BUILD_PACKAGE)
+
+ifeq (PackageInstaller,$(LOCAL_PACKAGE_NAME))
+ # Use the following include to make our test apk.
+ ifeq (,$(ONE_SHOT_MAKEFILE))
+ include $(call all-makefiles-under,$(LOCAL_PATH))
+ endif
+endif
diff --git a/packages/PackageInstaller/AndroidManifest.xml b/packages/PackageInstaller/AndroidManifest.xml
new file mode 100644
index 0000000000000..578207529880d
--- /dev/null
+++ b/packages/PackageInstaller/AndroidManifest.xml
@@ -0,0 +1,190 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/CleanSpec.mk b/packages/PackageInstaller/CleanSpec.mk
new file mode 100644
index 0000000000000..b84e1b65e75eb
--- /dev/null
+++ b/packages/PackageInstaller/CleanSpec.mk
@@ -0,0 +1,49 @@
+# Copyright (C) 2007 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+# If you don't need to do a full clean build but would like to touch
+# a file or delete some intermediate files, add a clean step to the end
+# of the list. These steps will only be run once, if they haven't been
+# run before.
+#
+# E.g.:
+# $(call add-clean-step, touch -c external/sqlite/sqlite3.h)
+# $(call add-clean-step, rm -rf $(PRODUCT_OUT)/obj/STATIC_LIBRARIES/libz_intermediates)
+#
+# Always use "touch -c" and "rm -f" or "rm -rf" to gracefully deal with
+# files that are missing or have been moved.
+#
+# Use $(PRODUCT_OUT) to get to the "out/target/product/blah/" directory.
+# Use $(OUT_DIR) to refer to the "out" directory.
+#
+# If you need to re-do something that's already mentioned, just copy
+# the command and add it to the bottom of the list. E.g., if a change
+# that you made last week required touching a file and a change you
+# made today requires touching the same file, just copy the old
+# touch step and add it to the end of the list.
+#
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
+
+# For example:
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/APPS/AndroidTests_intermediates)
+#$(call add-clean-step, rm -rf $(OUT_DIR)/target/common/obj/JAVA_LIBRARIES/core_intermediates)
+#$(call add-clean-step, find $(OUT_DIR) -type f -name "IGTalkSession*" -print0 | xargs -0 rm -f)
+#$(call add-clean-step, rm -rf $(PRODUCT_OUT)/data/*)
+
+# ************************************************
+# NEWER CLEAN STEPS MUST BE AT THE END OF THE LIST
+# ************************************************
diff --git a/packages/PackageInstaller/MODULE_LICENSE_APACHE2 b/packages/PackageInstaller/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000000000..e69de29bb2d1d
diff --git a/packages/PackageInstaller/NOTICE b/packages/PackageInstaller/NOTICE
new file mode 100644
index 0000000000000..c5b1efa7aac76
--- /dev/null
+++ b/packages/PackageInstaller/NOTICE
@@ -0,0 +1,190 @@
+
+ Copyright (c) 2005-2008, The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+
+
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
diff --git a/packages/PackageInstaller/OWNERS b/packages/PackageInstaller/OWNERS
new file mode 100644
index 0000000000000..252670a6fb135
--- /dev/null
+++ b/packages/PackageInstaller/OWNERS
@@ -0,0 +1,7 @@
+svetoslavganov@google.com
+moltmann@google.com
+toddke@google.com
+suprabh@google.com
+
+# For automotive related changes
+rogerxue@google.com
diff --git a/packages/PackageInstaller/PREUPLOAD.cfg b/packages/PackageInstaller/PREUPLOAD.cfg
new file mode 100644
index 0000000000000..94e464042243d
--- /dev/null
+++ b/packages/PackageInstaller/PREUPLOAD.cfg
@@ -0,0 +1,7 @@
+[Builtin Hooks]
+xmllint = true
+commit_msg_changeid_field = true
+
+[Hook Scripts]
+checkstyle_hook = ${REPO_ROOT}/prebuilts/checkstyle/checkstyle.py --sha ${PREUPLOAD_COMMIT}
+strings_lint_hook = ${REPO_ROOT}/frameworks/base/tools/stringslint/stringslint_sha.sh ${PREUPLOAD_COMMIT}
diff --git a/packages/PackageInstaller/proguard.flags b/packages/PackageInstaller/proguard.flags
new file mode 100644
index 0000000000000..46a929e383e05
--- /dev/null
+++ b/packages/PackageInstaller/proguard.flags
@@ -0,0 +1,8 @@
+# The support library contains references to newer platform versions.
+# Don't warn about those in case this app is linking against an older
+# platform version. We know about them, and they are safe.
+
+-keep class androidx.preference.Preference* {
+ *;
+}
+-dontwarn androidx.core.**
diff --git a/packages/PackageInstaller/res/anim/snackbar_enter.xml b/packages/PackageInstaller/res/anim/snackbar_enter.xml
new file mode 100644
index 0000000000000..96bf4d258ed21
--- /dev/null
+++ b/packages/PackageInstaller/res/anim/snackbar_enter.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/anim/snackbar_exit.xml b/packages/PackageInstaller/res/anim/snackbar_exit.xml
new file mode 100644
index 0000000000000..9aee177825dc8
--- /dev/null
+++ b/packages/PackageInstaller/res/anim/snackbar_exit.xml
@@ -0,0 +1,27 @@
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/color/btn_colored_background_material.xml b/packages/PackageInstaller/res/color/btn_colored_background_material.xml
new file mode 100644
index 0000000000000..a6cd0890706ab
--- /dev/null
+++ b/packages/PackageInstaller/res/color/btn_colored_background_material.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-hdpi/ic_fail.png b/packages/PackageInstaller/res/drawable-hdpi/ic_fail.png
new file mode 100644
index 0000000000000..30f75632cb92f
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-hdpi/ic_fail.png differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/ic_success.png b/packages/PackageInstaller/res/drawable-hdpi/ic_success.png
new file mode 100644
index 0000000000000..f36d491a6be7e
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-hdpi/ic_success.png differ
diff --git a/packages/PackageInstaller/res/drawable-hdpi/tab_unselected_holo.9.png b/packages/PackageInstaller/res/drawable-hdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000000000..19532ab10d4fe
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-hdpi/tab_unselected_holo.9.png differ
diff --git a/packages/PackageInstaller/res/drawable-mdpi/ic_fail.png b/packages/PackageInstaller/res/drawable-mdpi/ic_fail.png
new file mode 100644
index 0000000000000..12e269ea4fa66
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-mdpi/ic_fail.png differ
diff --git a/packages/PackageInstaller/res/drawable-mdpi/ic_success.png b/packages/PackageInstaller/res/drawable-mdpi/ic_success.png
new file mode 100644
index 0000000000000..51aab2e742427
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-mdpi/ic_success.png differ
diff --git a/packages/PackageInstaller/res/drawable-mdpi/tab_unselected_holo.9.png b/packages/PackageInstaller/res/drawable-mdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000000000..a2dbf42b74f7f
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-mdpi/tab_unselected_holo.9.png differ
diff --git a/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item.xml b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item.xml
new file mode 100644
index 0000000000000..96bf582c9beab
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_background.xml b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_background.xml
new file mode 100644
index 0000000000000..86d6b1f48a389
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_background.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_shape.xml b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_shape.xml
new file mode 100644
index 0000000000000..08cd3455949a3
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-television/grant_permissions_action_item_shape.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_negative_bg.xml b/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_negative_bg.xml
new file mode 100644
index 0000000000000..1ccc40abc8045
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_negative_bg.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_positive_bg.xml b/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_positive_bg.xml
new file mode 100644
index 0000000000000..786a35ba21c6f
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/accept_deny_dialog_positive_bg.xml
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/action_negative_bg.xml b/packages/PackageInstaller/res/drawable-watch/action_negative_bg.xml
new file mode 100644
index 0000000000000..994ae8e2d4a85
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/action_negative_bg.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/action_positive_bg.xml b/packages/PackageInstaller/res/drawable-watch/action_positive_bg.xml
new file mode 100644
index 0000000000000..3e8590f261cae
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/action_positive_bg.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/cancel_button.xml b/packages/PackageInstaller/res/drawable-watch/cancel_button.xml
new file mode 100644
index 0000000000000..8e4d2d48f8a54
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/cancel_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/confirm_button.xml b/packages/PackageInstaller/res/drawable-watch/confirm_button.xml
new file mode 100644
index 0000000000000..d1bfae331bf55
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/confirm_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/deny_button.xml b/packages/PackageInstaller/res/drawable-watch/deny_button.xml
new file mode 100644
index 0000000000000..0a2e1ae16931e
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/deny_button.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/ic_cc_checkmark.xml b/packages/PackageInstaller/res/drawable-watch/ic_cc_checkmark.xml
new file mode 100644
index 0000000000000..832e090fc3848
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/ic_cc_checkmark.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/ic_cc_clear.xml b/packages/PackageInstaller/res/drawable-watch/ic_cc_clear.xml
new file mode 100644
index 0000000000000..06bb30b3207ea
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/ic_cc_clear.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-watch/ic_cc_deny.xml b/packages/PackageInstaller/res/drawable-watch/ic_cc_deny.xml
new file mode 100644
index 0000000000000..971dfcc84f3f6
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable-watch/ic_cc_deny.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable-xhdpi/ic_fail.png b/packages/PackageInstaller/res/drawable-xhdpi/ic_fail.png
new file mode 100644
index 0000000000000..6deb808c81f98
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-xhdpi/ic_fail.png differ
diff --git a/packages/PackageInstaller/res/drawable-xhdpi/ic_success.png b/packages/PackageInstaller/res/drawable-xhdpi/ic_success.png
new file mode 100644
index 0000000000000..25f8658f5c39e
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-xhdpi/ic_success.png differ
diff --git a/packages/PackageInstaller/res/drawable-xhdpi/tab_unselected_holo.9.png b/packages/PackageInstaller/res/drawable-xhdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000000000..9465173781eaa
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-xhdpi/tab_unselected_holo.9.png differ
diff --git a/packages/PackageInstaller/res/drawable-xxhdpi/tab_unselected_holo.9.png b/packages/PackageInstaller/res/drawable-xxhdpi/tab_unselected_holo.9.png
new file mode 100644
index 0000000000000..8fcecf76c1997
Binary files /dev/null and b/packages/PackageInstaller/res/drawable-xxhdpi/tab_unselected_holo.9.png differ
diff --git a/packages/PackageInstaller/res/drawable/button_ripple_bg.xml b/packages/PackageInstaller/res/drawable/button_ripple_bg.xml
new file mode 100644
index 0000000000000..dabacb10c0111
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/button_ripple_bg.xml
@@ -0,0 +1,23 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_android_92.xml b/packages/PackageInstaller/res/drawable/ic_android_92.xml
new file mode 100644
index 0000000000000..1d3791c1b26d1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_android_92.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_arrow_back.xml b/packages/PackageInstaller/res/drawable/ic_arrow_back.xml
new file mode 100644
index 0000000000000..81da87fd1d5d2
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_arrow_back.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_dialog_alert_material.xml b/packages/PackageInstaller/res/drawable/ic_dialog_alert_material.xml
new file mode 100644
index 0000000000000..b01a6d683c18c
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_dialog_alert_material.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_done_92.xml b/packages/PackageInstaller/res/drawable/ic_done_92.xml
new file mode 100644
index 0000000000000..185b274dc36a1
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_done_92.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_error.xml b/packages/PackageInstaller/res/drawable/ic_error.xml
new file mode 100644
index 0000000000000..28612a1c87ccd
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_error.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_fail_material.xml b/packages/PackageInstaller/res/drawable/ic_fail_material.xml
new file mode 100644
index 0000000000000..9d53fdc0180d9
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_fail_material.xml
@@ -0,0 +1,18 @@
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_file_download.xml b/packages/PackageInstaller/res/drawable/ic_file_download.xml
new file mode 100644
index 0000000000000..7ea91f5baa2da
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_file_download.xml
@@ -0,0 +1,28 @@
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_info_outline.xml b/packages/PackageInstaller/res/drawable/ic_info_outline.xml
new file mode 100644
index 0000000000000..86597586d9e6b
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_info_outline.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_lock.xml b/packages/PackageInstaller/res/drawable/ic_lock.xml
new file mode 100644
index 0000000000000..396bd988eb80a
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_lock.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_more_items.xml b/packages/PackageInstaller/res/drawable/ic_more_items.xml
new file mode 100644
index 0000000000000..5fdcdcef7317d
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_more_items.xml
@@ -0,0 +1,29 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/drawable/ic_perm_device_info.xml b/packages/PackageInstaller/res/drawable/ic_perm_device_info.xml
new file mode 100644
index 0000000000000..ef91c74620ac2
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_perm_device_info.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_remove.xml b/packages/PackageInstaller/res/drawable/ic_remove.xml
new file mode 100644
index 0000000000000..dd46eda652673
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_remove.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml b/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml
new file mode 100644
index 0000000000000..c90a33ed68b2f
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_report_problem_92.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml b/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml
new file mode 100644
index 0000000000000..b24a5d434c54a
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_settings_multiuser.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_success_material.xml b/packages/PackageInstaller/res/drawable/ic_success_material.xml
new file mode 100644
index 0000000000000..ebcce3b5532c8
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_success_material.xml
@@ -0,0 +1,18 @@
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/ic_toc.xml b/packages/PackageInstaller/res/drawable/ic_toc.xml
new file mode 100644
index 0000000000000..66c476bef832b
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/ic_toc.xml
@@ -0,0 +1,24 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/drawable/rectangle_ripple_mask.xml b/packages/PackageInstaller/res/drawable/rectangle_ripple_mask.xml
new file mode 100644
index 0000000000000..69eaf8bd4e4b2
--- /dev/null
+++ b/packages/PackageInstaller/res/drawable/rectangle_ripple_mask.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/PackageInstaller/res/layout-television/app_details.xml b/packages/PackageInstaller/res/layout-television/app_details.xml
new file mode 100644
index 0000000000000..86923c5cb54ac
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/app_details.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-television/grant_permissions.xml b/packages/PackageInstaller/res/layout-television/grant_permissions.xml
new file mode 100644
index 0000000000000..44583bfbeb493
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/grant_permissions.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-television/header.xml b/packages/PackageInstaller/res/layout-television/header.xml
new file mode 100644
index 0000000000000..56e35db7109c7
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/header.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-television/permissions_frame.xml b/packages/PackageInstaller/res/layout-television/permissions_frame.xml
new file mode 100644
index 0000000000000..1dcda8d4abe61
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/permissions_frame.xml
@@ -0,0 +1,47 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-television/uninstall_progress.xml b/packages/PackageInstaller/res/layout-television/uninstall_progress.xml
new file mode 100644
index 0000000000000..e24f63b5e1083
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-television/uninstall_progress.xml
@@ -0,0 +1,109 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-watch/accept_deny_dialog.xml b/packages/PackageInstaller/res/layout-watch/accept_deny_dialog.xml
new file mode 100644
index 0000000000000..1a0509eede2d2
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-watch/accept_deny_dialog.xml
@@ -0,0 +1,111 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-watch/wear_review_permission_action_pref.xml b/packages/PackageInstaller/res/layout-watch/wear_review_permission_action_pref.xml
new file mode 100644
index 0000000000000..2b10c4dbb0c66
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-watch/wear_review_permission_action_pref.xml
@@ -0,0 +1,25 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout-watch/wear_review_permission_title_pref.xml b/packages/PackageInstaller/res/layout-watch/wear_review_permission_title_pref.xml
new file mode 100644
index 0000000000000..67928356116f6
--- /dev/null
+++ b/packages/PackageInstaller/res/layout-watch/wear_review_permission_title_pref.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/car_app_permissions.xml b/packages/PackageInstaller/res/layout/car_app_permissions.xml
new file mode 100644
index 0000000000000..e052b67af81f4
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/car_app_permissions.xml
@@ -0,0 +1,68 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/grant_permissions.xml b/packages/PackageInstaller/res/layout/grant_permissions.xml
new file mode 100644
index 0000000000000..f2a041fd0f5f3
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/grant_permissions.xml
@@ -0,0 +1,141 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/grant_permissions_content.xml b/packages/PackageInstaller/res/layout/grant_permissions_content.xml
new file mode 100644
index 0000000000000..caa1d7665ab59
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/grant_permissions_content.xml
@@ -0,0 +1,105 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/header.xml b/packages/PackageInstaller/res/layout/header.xml
new file mode 100644
index 0000000000000..45e6972c41289
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/header.xml
@@ -0,0 +1,54 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/install_confirm.xml b/packages/PackageInstaller/res/layout/install_confirm.xml
new file mode 100644
index 0000000000000..6be46fc88e9bd
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_confirm.xml
@@ -0,0 +1,91 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/install_failed.xml b/packages/PackageInstaller/res/layout/install_failed.xml
new file mode 100644
index 0000000000000..d000ee9aa6075
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_failed.xml
@@ -0,0 +1,96 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/install_installing.xml b/packages/PackageInstaller/res/layout/install_installing.xml
new file mode 100644
index 0000000000000..a043a0146c808
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_installing.xml
@@ -0,0 +1,108 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/install_staging.xml b/packages/PackageInstaller/res/layout/install_staging.xml
new file mode 100644
index 0000000000000..e3022e7afb208
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_staging.xml
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/install_success.xml b/packages/PackageInstaller/res/layout/install_success.xml
new file mode 100644
index 0000000000000..fee6bede578a6
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/install_success.xml
@@ -0,0 +1,101 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/label.xml b/packages/PackageInstaller/res/layout/label.xml
new file mode 100644
index 0000000000000..e1a51434c1ed8
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/label.xml
@@ -0,0 +1,22 @@
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/loading_container.xml b/packages/PackageInstaller/res/layout/loading_container.xml
new file mode 100644
index 0000000000000..5a165deee5832
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/loading_container.xml
@@ -0,0 +1,36 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/permissions_frame.xml b/packages/PackageInstaller/res/layout/permissions_frame.xml
new file mode 100644
index 0000000000000..156af57e897a5
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/permissions_frame.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/permissions_list.xml b/packages/PackageInstaller/res/layout/permissions_list.xml
new file mode 100644
index 0000000000000..5e3ae0f5c23da
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/permissions_list.xml
@@ -0,0 +1,31 @@
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/preference_category_material.xml b/packages/PackageInstaller/res/layout/preference_category_material.xml
new file mode 100644
index 0000000000000..f62260bc1900b
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/preference_category_material.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/preference_permissions.xml b/packages/PackageInstaller/res/layout/preference_permissions.xml
new file mode 100644
index 0000000000000..2a03067c8a7a0
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/preference_permissions.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/preference_permissions_switch.xml b/packages/PackageInstaller/res/layout/preference_permissions_switch.xml
new file mode 100644
index 0000000000000..50e2c99f3a014
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/preference_permissions_switch.xml
@@ -0,0 +1,80 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/review_permissions.xml b/packages/PackageInstaller/res/layout/review_permissions.xml
new file mode 100644
index 0000000000000..6a1c6b3ea774d
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/review_permissions.xml
@@ -0,0 +1,112 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/layout/uninstall_confirm.xml b/packages/PackageInstaller/res/layout/uninstall_confirm.xml
new file mode 100644
index 0000000000000..4c81771674643
--- /dev/null
+++ b/packages/PackageInstaller/res/layout/uninstall_confirm.xml
@@ -0,0 +1,130 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values-af-television/strings.xml b/packages/PackageInstaller/res/values-af-television/strings.xml
new file mode 100644
index 0000000000000..32cf00f9673bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Weier en moenie weer vra nie"
+ "Jy kan dit later verander in Instellings > Programme"
+ "%1$s / %2$s"
+ "Wys stelselprogramme"
+ "Programtoestemmings"
+ "Programtoestemmings"
+ "%1$stoestemmings"
+ "Bykomende toestemmings"
+ "%1$stoestemmings"
+
diff --git a/packages/PackageInstaller/res/values-af-watch/strings.xml b/packages/PackageInstaller/res/values-af-watch/strings.xml
new file mode 100644
index 0000000000000..a62540f65ff59
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Weier; moenie weer vra nie"
+ "%1$s / %2$s"
+ "Wys stelselprogramme"
+ "Onveranderbaar"
+ "Ja"
+ "Kanselleer"
+
diff --git a/packages/PackageInstaller/res/values-af/strings.xml b/packages/PackageInstaller/res/values-af/strings.xml
new file mode 100644
index 0000000000000..0d41c18ee9247
--- /dev/null
+++ b/packages/PackageInstaller/res/values-af/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pakketinstalleerder"
+ "Volgende"
+ "Installeer"
+ "Klaar"
+ "Kanselleer"
+ "Installeer tans…"
+ "Installeer tans %1$s …"
+ "Program geïnstalleer."
+ "Wil jy hierdie program installeer? Dit sal kan ingaan by:"
+ "Wil jy hierdie program installeer? Dit vereis nie enige spesiale toegang nie."
+ "Wil jy \'n opdatering vir die bestaande program installeer? Jou bestaande data sal nie verlore gaan nie. Die opgedateerde program sal kan ingaan by:"
+ "Wil jy \'n opdatering vir hierdie ingeboude program installeer? Jou bestaande data sal nie verlore gaan nie. Die opgedateerde program sal kan ingaan by:"
+ "Wil jy \'n opdatering na hierdie bestaande program installeer? Jou bestaande data sal nie verlore raak nie. Dit vereis nie enige spesiale toegang nie."
+ "Wil jy \'n opdatering na hierdie ingeboude program installeer? Jou bestaande data sal nie verlore raak nie. Dit vereis nie enige spesiale toegang nie."
+ "Program nie geïnstalleer nie."
+ "Die installering van die pakket is geblokkeer."
+ "Program is nie geïnstalleer nie omdat pakket met \'n bestaande pakket bots."
+ "Program is nie geïnstalleer nie omdat dit nie met jou tablet versoenbaar is nie."
+ "Hierdie program is nie met jou TV versoenbaar nie."
+ "Program is nie geïnstalleer nie omdat dit nie met jou foon versoenbaar is nie."
+ "Program is nie geïnstalleer nie omdat pakket ongeldig blyk te wees."
+ "%1$s kon nie op jou tablet geïnstalleer word nie."
+ "%1$s kon nie op jou TV geïnstalleer word nie."
+ "%1$s kon nie op jou foon geïnstalleer word nie."
+ "Open"
+ "Jou administrateur laat nie toe dat programme wat by onbekende bronne verkry is, geïnstalleer word nie"
+ "Hierdie gebruiker kan nie onbekende programme installeer nie"
+ "Hierdie gebruiker word nie toegelaat om programme te installeer nie"
+ "OK"
+ "Bestuur programme"
+ "Geen spasie oor nie"
+ "%1$s kon nie geïnstalleer word nie. Maak \'n bietjie plek en probeer weer."
+ "Program nie gevind nie"
+ "Die program is nie in die lys van geïnstalleerde programme gevind nie."
+ "Nie toegelaat nie"
+ "Die huidige gebruiker mag nie hierdie deïnstallering uitvoer nie."
+ "Fout"
+ "Program kon nie gedeïnstalleer word nie."
+ "Deïnstalleer program"
+ "Deïnstalleer opdatering"
+ "%1$s is deel van die volgende program:"
+ "Wil jy hierdie program deïnstalleer?"
+ "Wil jy hierdie program vir ""alle"" gebruikers deïnstalleer? Die program en sy data sal vir ""alle"" gebruikers op hierdie toestel verwyder word."
+ "Wil jy hierdie program vir die gebruiker %1$s deïnstalleer?"
+ "Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word."
+ "Vervang hierdie program met die fabriekweergawe? Alle data sal verwyder word. Dit beïnvloed alle gebruikers van hierdie toestel, insluitend dié met werkprofiele."
+ "Voer tans deïnstallerings uit"
+ "Mislukte installerings"
+ "Deïnstalleer tans…"
+ "Deïnstalleer tans %1$s …"
+ "Deïnstallering klaar."
+ "Het %1$s gedeïnstalleer"
+ "Deïnstallasie onsuksesvol."
+ "Kon nie %1$s deïnstalleer nie."
+ "Kan nie aktiewe toesteladministrasieprogram deïnstalleer nie"
+ "Kan nie aktiewe toesteladministrasieprogram vir %1$s deïnstalleer nie"
+ "Dié program word vereis vir sommige gebruikers of profiele en is vir ander gedeïnstalleer"
+ "Hierdie program is nodig vir jou profiel en kan nie gedeïnstalleer word nie."
+ "Jou toesteladministrateur vereis die program; dit kan nie deïnstalleer word nie."
+ "Bestuur toesteladministrasieprogramme"
+ "Bestuur gebruikers"
+ "%1$s kon nie deïnstalleer word nie."
+ "Kon nie die pakket ontleed nie."
+ "Nuut"
+ "Alle"
+ "Privaatheid"
+ "Toesteltoegang"
+ "Hierdie opdatering vereis geen nuwe toestemmings nie."
+ "Weier"
+ "Meer inligting"
+ "Weier in elk geval"
+ "%1$s van %2$s"
+ "Laat <b>%1$s</b> toe om %2$s?"
+ "Laat <b>%1$s</b> altyd toe om %2$s?"
+ "Net terwyl program gebruik word"
+ "Altyd"
+ "Weier en moenie weer vra nie"
+ "%1$d is gedeaktiveer"
+ "alles is gedeaktiveer"
+ "geen is gedeaktiveer nie"
+ "Laat toe"
+ "Programme"
+ "Programtoestemmings"
+ "Moenie weer vra nie"
+ "Geen toestemmings nie"
+ "Bykomende toestemmings"
+ "Maak programinligting oop"
+
+ Nog %1$d
+ Nog %1$d
+
+ "Hierdie program is vir \'n ouer weergawe van Android ontwerp. As toestemming geweier word, kan dit veroorsaak dat dit nie meer soos beplan funksioneer nie."
+ "voer \'n onbekende handeling uit"
+ "%1$d uit %2$d programme toegelaat"
+ "Wys stelsel"
+ "Versteek stelsel"
+ "Geen programme nie"
+ "Ligginginstellings"
+ "%1$s is \'n verskaffer van liggingdienste vir hierdie toestel. Liggingtoegang kan vanuit ligginginstellings verander word."
+ "As jy hierdie toestemming weier, sal basiese kenmerke van jou toestel dalk nie meer soos bedoel werk nie."
+ "Afgedwing deur beleid"
+ "Agtergrondtoegang is gedeaktiveer volgens beleid"
+ "Agtergrondtoegang is geaktiveer volgens beleid"
+ "Voorgrondtoegang is geaktiveer volgens beleid"
+ "Beheer deur administrateur"
+
+
+
+ "Altyd"
+ "Net as program gebruik word"
+ "Nooit"
+ "Laai tans …"
+ "Alle toestemmings"
+ "Ander programvermoëns"
+ "Toestemmingsversoek"
+ "Skermoorlegger bespeur"
+ "Om hierdie toestemminginstelling te verander, moet jy eers die skermoorlegger by Instellings > Programme afskakel"
+ "Maak instellings oop"
+ "Android Wear"
+ "Installeer- en deïnstalleerhandelinge word nie in Wear gesteun nie."
+ "Kies waartoe <b>%1$s</b> toegang mag kry"
+ "<b>%1$s</b> is opgedateer. Kies waartoe hierdie program toegang mag kry."
+ "Kanselleer"
+ "Gaan voort"
+ "Nuwe toestemmings"
+ "Huidige toestemmings"
+ "Voer tans program uit …"
+ "Onbekend"
+ "Jou tablet word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."
+ "Jou TV word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."
+ "Jou foon word vir jou veiligheid nie toegelaat om onbekende programme van hierdie bron af te installeer nie."
+ "Jou foon en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou foon of verlies van data wat uit sy gebruik kan spruit."
+ "Jou tablet en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou tablet of verlies van data wat uit sy gebruik kan spruit."
+ "Jou TV en persoonlike data is meer kwesbaar vir aanvalle deur onbekende programme. Deur hierdie program te installeer, stem jy in dat jy verantwoordelik is vir enige skade aan jou TV of verlies van data wat uit sy gebruik kan spruit."
+ "Gaan voort"
+ "Instellings"
+ "Installeer/deïnstalleer Wear-programme"
+
diff --git a/packages/PackageInstaller/res/values-am-television/strings.xml b/packages/PackageInstaller/res/values-am-television/strings.xml
new file mode 100644
index 0000000000000..e4f23c1bd276c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "አይቀበሉና እንደገና ይጠይቁ"
+ "ይሄንን በኋላ ላይ በቅንብሮችና መተግበሪያዎች ውስጥ ሊቀይሩት ይችላሉ"
+ "%1$s / %2$s"
+ "የስርዓት መተግበሪያዎችን አሳይ"
+ "የመተግበሪያ ፈቃዶች"
+ "የመተግበሪያ ፈቃዶች"
+ "%1$s ፈቃዶች"
+ "ተጨማሪ ፈቃዶች"
+ "%1$s ፈቃዶች"
+
diff --git a/packages/PackageInstaller/res/values-am-watch/strings.xml b/packages/PackageInstaller/res/values-am-watch/strings.xml
new file mode 100644
index 0000000000000..1b01ddd39bbf3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ከልክል፣ ዳግም አትጠይቅ"
+ "%1$s / %2$s"
+ "የስርዓት መተግበሪያዎችን አሳይ"
+ "ሊለወጥ አይችልም"
+ "አዎ"
+ "ይቅር"
+
diff --git a/packages/PackageInstaller/res/values-am/strings.xml b/packages/PackageInstaller/res/values-am/strings.xml
new file mode 100644
index 0000000000000..d4c5076514d56
--- /dev/null
+++ b/packages/PackageInstaller/res/values-am/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ጥቅል ጫኝ"
+ "ቀጣይ"
+ "ጫን"
+ "ተከናውኗል"
+ "ይቅር"
+ "በመጫን ላይ…"
+ "%1$sን በመጫን ላይ…"
+ "መተግበሪያ ተጭኗል፡፡"
+ "ይህንን መተግበሪያ መጫን ይፈልጋሉ? ወደዚህ መዳረሻ ያገኛል፦"
+ "ይህንን መተግበሪያ መጫን ይፈልጋሉ? ምንም የተለየ መዳረሻ አይጠይቅም።"
+ "ለእዚህ ነባር መተግበሪያ ማዘመኛ መጫን ይፈልጋሉ? የነበረው ውሂብህ አይጠፋም። የዘመነው መተግበሪያ ወደዚህ መዳረሻ ያገኛል፦"
+ "ለእዚህ አብሮ ለተሰራ መተግበሪያ ማዘመኛ መጫን ይፈልጋሉ? የነበረው ውሂብዎ አይጠፋም። የዘመነው መተግበሪያ ወደዚህ መዳረሻ ያገኛል፦"
+ "ለዚህ ነባር መተግበሪያ ዝማኔ መጫን ይፈልጋሉ? ነባር ውሂብዎ አይጠፉም። ምንም የተለየ መዳረሻ አይፈልግም።"
+ "ለዚህ አብሮ ለተሰራ መተግበሪያ ዝማኔ መጫን ይፈልጋሉ? ነባር ውሂብዎ አይጠፉም። ምንም የተለየ መዳረሻ አይፈልግም።"
+ "ትግበራ አልተጫነም።"
+ "ጥቅሉ እንዳይጫን ታግዷል።"
+ "እንደ ጥቅል ያልተጫነ መተግበሪያ ከነባር ጥቅል ጋር ይጋጫል።"
+ "እንደ መተግበሪያ ያልተጫነ መተግበሪያ ከጡባዊዎ ጋር ተኳሃኝ አይደለም።"
+ "ይሄ መተግበሪያ ከእርስዎ ቴሌቪዥን ጋር ተኳሃኝ አይደለም።"
+ "እንደ መተግበሪያ ያልተጫነ መተግበሪያ ከስልክዎ ጋር ተኳሃኝ አይደለም።"
+ "እንደ ጥቅል ያልተጫነ መተግበሪያ ልክ ያልሆነ ይመስላል።"
+ "%1$s በዚህ ስልክ ላይ መጫን አልተቻለም።"
+ "%1$s በእርስዎ ቴሌቪዥን ላይ ሊጫን አልቻለም።"
+ "%1$sበዚህ ስልክ ላይ መጫን አልተቻለም።"
+ "ክፈት"
+ "የእርስዎ አስተዳዳሪ ካልታወቁ ምንጮች የመጡ መተግበሪያዎች እንዲጫኑ አይፈቅድም"
+ "ያልታወቁ መተግበሪያዎች በዚህ ተጠቃሚ ሊጫኑ አይችሉም"
+ "ይህ ተጠቃሚ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም"
+ "እሺ"
+ "መተግበሪያዎች አስተዳድር"
+ "ቦታ ሞልቷል"
+ "%1$sለመጫን አልቻለም። ትንሽ ቦታ አስለቅቅ እና እንደገና ሞክር፡፡"
+ "ትግበራ አልተገኘም"
+ "መተግበሪያው በተጫኑ መተግበሪያዎች ዝርዝር ውስጥ አልተገኘም።"
+ "አይፈቀድም"
+ "አሁን ያለው ተጠቃሚ ይህን ማራገፍ ሥራ እንዲያከናውን አይፈቀድለትም።"
+ "ስሕተት"
+ "መተግበሪያ ሊራገፍ አልተቻለም"
+ "ትግበራ አራግፍ"
+ "ማዘመን አራግፍ"
+ "%1$sየሚከተለው ትግበራ አካል ነው፡"
+ "ይሄን መተግበሪያ ማራገፍ ይፈልጋሉ?"
+ "ይህን መተግበሪያ ""ለሁሉም"" ተጠቃሚዎች መጫን ይፈልጋሉ? መተግበሪያው እና ውሂቡ በመሣሪያው ላይ ካሉ ""ሁሉም"" ተጠቃሚዎች ይሰረዛሉ።"
+ "ይህን መተግበሪያ ለተጠቃሚ %1$s ማራገፍ ይፈልጋሉ?"
+ "ይህ መተግበሪያ በፋብሪክው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል።"
+ "ይህ መተግበሪያ በፋብሪክው ስሪት ይተካ? ሁሉም ውሂብ ይወገዳል። እነዚያን የሥራ መገለጫዎች ያላቸውን ጨምሮ ሁሉንም በዚህ መሣሪያ ላይ ባሉ ተጠቃሚዎች ላይ ተጽዕኖ ያሳርፍባቸዋል።"
+ "በማሄድ ላይ ያሉ ማራገፎች"
+ "ያልተሳኩ ማራገፎች"
+ "ባለመጫንላይ"
+ "%1$sን በማራገፍ ላይ…"
+ "አራግፍ ተጠናቋል"
+ "%1$s ተራግፏል"
+ "ማራገፍ አልተሳካም፡፡"
+ "%1$sን ማራገፍ ስኬታማ አልነበረም።"
+ "ገባሪ የመሣሪያ አስተዳደር መተግበሪያን ማራገፍ አይቻልም"
+ "ለ%1$s ገባሪ የመሣሪያ አስተዳደር መተግበሪያን ማራገፍ አይቻልም"
+ "ይህ መተግበሪያ ለአንዳንድ ተጠቃሚዎች ወይም መገለጫዎች ያስፈልጋል እና ለሌሎች ተራግፏል"
+ "ይህ መተግበሪያ ለእርስዎ መገለጫዎ ያስፈልጋል እና ሊራገፍ አይችልም።"
+ "ይህ መተግበሪያ በመሣሪያዎ አስተዳዳሪ የሚፈለግ እና ሊራገፍ የማይችል ነው።"
+ "የመሣሪያ አስተዳደር መተግበሪያዎችን ያስተዳድሩ"
+ "ተጠቃሚዎችን ያስተዳድሩ"
+ "%1$sማራገፍ አልተቻለም"
+ "አካታቹን መተንተን ችግር ነበረ።"
+ "አዲስ"
+ "ሁሉም"
+ "ግላዊነት"
+ "የመሳሪያ መዳረሻ"
+ "ይህ ዝማኔ ምንም አዲስ ፈቃድ አያስፈልገውም።"
+ "ከልክል"
+ "ተጨማሪ መረጃ"
+ "ለማንኛውም ከልክል"
+ "%1$s ከ%2$s"
+ "<b>%1$s</b> %2$s እንዲከናወን ይፈቀድለት?"
+ "ሁልጊዜ <b>%1$s</b> ወደ %2$s ይፈቀድ?"
+ "መተግበሪያን በመጠቀም ላይ ሲኮን ብቻ"
+ "ሁልጊዜ"
+ "አትቀበል እና እንደገና አትጠይቅ"
+ "%1$d ተሰናክሏል"
+ "ሁሉም ተሰናክሏል"
+ "ምንም አልተሰናከለም"
+ "ፍቀድ"
+ "መተግበሪያዎች"
+ "የመተግበሪያ ፈቃዶች"
+ "ዳግም አትጠይቅ"
+ "ምንም ፍቃዶች የሉም"
+ "ተጨማሪ ፈቃዶች"
+ "የመተግበሪያ መረጃን ክፈት"
+
+ %1$d ተጨማሪ
+ %1$d ተጨማሪ
+
+ "ይህ መተግበሪያ ለAndroid አሮጌ ስሪት የተነደፈ ነበር። ፈቃድ መከልከል እንደሚፈለገው ከእንግዲህ እንዳይሰራ ሊያደርገው ይችላል።"
+ "ያልታወቀ እርምጃ ያከናውናል"
+ "%1$d ከ%2$d መተግበሪያዎች ተፈቅዶላቸዋል"
+ "ስርዓትን አሳይ"
+ "ስርዓትን ደብቅ"
+ "ምንም መተግበሪያዎች የሉም"
+ "የአካባቢ ቅንብሮች"
+ "%1$s የዚህ መሳሪያ አካባቢ አገልግሎቶች አቅራቢ ነው። የአካባቢ መዳረሻ ከአካባቢ ቅንብሮች ሊሻሻል ይችላል።"
+ "ይህን ፍቃድ ከከለከሉ የመሳሪያዎ መሰረታዊ ባህሪያት ከዚህ በኋላ እንደተፈለገው ላይሰሩ ይችላሉ።"
+ "በመመሪያ ተፈጻሚ የሆነ"
+ "የጀርባ መዳረሻ በመመሪያ ተሰናክሏል"
+ "የጀርባ መዳረሻ በመመሪያ ነቅቷል"
+ "የፊት መዳረሻ በመመሪያ ነቅቷል"
+ "በአስተዳዳሪ ቁጥጥር የሚደረግበት"
+
+
+
+ "ዘወትር"
+ "መተግበሪያን በስራ ላይ ሲሆን ብቻ"
+ "በጭራሽ"
+ "በመጫን ላይ…"
+ "ሁሉም ፍቃዶች"
+ "ሌሎች የመተግበሪያ ችሎታዎች"
+ "የፍቃድ ጥያቄ"
+ "የማያ ገጽ ተደራቢ ተገኝቷል"
+ "ይህን የፍቃድ ቅንብር ለመቀየር መጀመሪያ የማያ ገጽ ተደራቢውን ከቅንብሮች > መተግበሪያዎች ማጥፋት አለብዎ"
+ "ቅንብሮችን ክፈት"
+ "Android Wear"
+ "በWear ላይ የመጫን/ማራገፍ እርምጃዎች አይደገፉም።"
+ "<b>%1$s</b> ምን መድረስ እንደሚችል ይምረጡ"
+ "<b>%1$s</b> ተዘምኗል። ይህ መተግበሪያ ምን መድረስ እንደሚችል ይምረጡ።"
+ "ይቅር"
+ "ቀጥል"
+ "አዲስ ፍቃዶች"
+ "የአሁኖቹ ፍቃዶች"
+ "መተግበሪያን በማዘጋጀት ላይ…"
+ "ያልታወቀ"
+ "ለእርስዎ ደህንነት ሲባል የእርስዎ ጡባዊ ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"
+ "ለእርስዎ ደህንነት ሲባል የእርስዎ ቴሌቪዥን ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"
+ "ለእርስዎ ደህንነት ሲባል የእርስዎ ስልክ ከዚህ ምንጭ የመጡ ያልታወቁ መተግበሪያዎችን እንዲጭን አልተፈቀደለትም።"
+ "የእርስዎ ስልክ እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይልበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ስልክ ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"
+ "የእርስዎ ጡባዊ እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ጡባዊ ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"
+ "የእርስዎ ቴሌቪዥን እና የግል ውሂብ በማይታወቁ መተግበሪያዎች ለሚደርሱ ጥቃቶች በይበልጥ ተጋላጭ ናቸው። ይህን መተግበሪያ በመጫንዎ በእርስዎ ቴሌቪዥን ላይ ለሚደርስ ማናቸውም ጉዳት ወይም መተግበሪያውን በመጠቀም ለሚከሰት የውሂብ መጥፋት ኃላፊነቱን እንደሚወስዱ ተስማምተዋል።"
+ "ቀጥል"
+ "ቅንብሮች"
+ "የWear መተግበሪያዎችን መጫን/ማራገፍ"
+
diff --git a/packages/PackageInstaller/res/values-ar-television/strings.xml b/packages/PackageInstaller/res/values-ar-television/strings.xml
new file mode 100644
index 0000000000000..9297b889ccd4e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "رفض وعدم طرح السؤال مرة أخرى"
+ "يمكنك تغيير ذلك لاحقًا من خلال الإعدادات > التطبيقات"
+ "%1$s / %2$s"
+ "عرض تطبيقات النظام"
+ "أذونات التطبيق"
+ "أذونات التطبيق"
+ "أذونات %1$s"
+ "أذونات إضافية"
+ "أذونات %1$s"
+
diff --git a/packages/PackageInstaller/res/values-ar-watch/strings.xml b/packages/PackageInstaller/res/values-ar-watch/strings.xml
new file mode 100644
index 0000000000000..19930ea71c58a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "الرفض وعدم السؤال مرة أخرى"
+ "%1$s / %2$s"
+ "عرض تطبيقات النظام"
+ "لا يمكن التغيير"
+ "نعم"
+ "إلغاء"
+
diff --git a/packages/PackageInstaller/res/values-ar/strings.xml b/packages/PackageInstaller/res/values-ar/strings.xml
new file mode 100644
index 0000000000000..613481eda3e1d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ar/strings.xml
@@ -0,0 +1,160 @@
+
+
+
+
+ "أداة تثبيت الحزم"
+ "التالي"
+ "تثبيت"
+ "تم"
+ "إلغاء"
+ "جارٍ التثبيت..."
+ "جارٍ تثبيت %1$s…"
+ "تم تثبيت التطبيق."
+ "هل تريد تثبيت هذا التطبيق؟ سيكون بإمكانه الدخول إلى:"
+ "هل تريد تثبيت هذا التطبيق؟ إنه لا يتطلب أي دخول خاص."
+ "هل تريد تثبيت تحديث لهذا التطبيق الحالي؟ لن تفقد بياناتك الحالية. سيكون بإمكان التطبيق المحدّث الدخول إلى:"
+ "هل تريد تثبيت تحديث لهذا التطبيق المضمن؟ لن تفقد بياناتك الحالية. سيكون بإمكان التطبيق المحدّث الدخول إلى:"
+ "هل تريد تثبيت تحديث لهذا التطبيق الحالي؟ لن يتم فقد بياناتك الحالية. كما أنه لا يتطلب أي دخول خاص."
+ "هل تريد تثبيت تحديث لهذا التطبيق المضمن؟ لن يتم فقد بياناتك الحالية. كما أنه لا يتطلب أي دخول خاص."
+ "التطبيق ليس مثبتًا."
+ "تم حظر تثبيت الحزمة."
+ "لم يتم تثبيت التطبيق لأن حزمة التثبيت تتعارض مع حزمة حالية."
+ "لم يتم تثبيت التطبيق لأنه ليس متوافقًا مع جهازك اللوحي."
+ "هذا التطبيق لا يتوافق مع جهاز التلفزيون."
+ "لم يتم تثبيت التطبيق لأنه ليس متوافقًا مع هاتفك."
+ "لم يتم تثبيت التطبيق لأن الحزمة تبدو غير صالحة."
+ "تعذر تثبيت %1$s على جهازك اللوحي."
+ "تعذر تثبيت %1$s على جهاز التلفزيون."
+ "تعذر تثبيت %1$s على هاتفك."
+ "فتح"
+ "لا يسمح المشرف بتثبيت التطبيقات التي يتم الحصول عليها من مصادر غير معروفة"
+ "يتعذر على هذا المستخدم تثبيت التطبيقات غير المعروفة"
+ "غير مسموح لهذا المستخدم بتثبيت التطبيقات"
+ "موافق"
+ "إدارة التطبيقات"
+ "نفدت مساحة التخزين"
+ "تعذر تثبيت %1$s يُرجى تحرير بعض المساحة والمحاولة مرة أخرى."
+ "لم يتم العثور على التطبيق"
+ "لم يتم العثور على التطبيق في قائمة التطبيقات المثبتة."
+ "غير مسموح به"
+ "غير مسموح للمستخدم الحالي بتنفيذ عملية إلغاء التثبيت هذه."
+ "الخطأ"
+ "تعذر إلغاء تثبيت التطبيق."
+ "إلغاء تثبيت التطبيق"
+ "إزالة التحديث"
+ "%1$s هو جزء من التطبيق التالي:"
+ "هل تريد إزالة هذا التطبيق؟"
+ "هل تريد إزالة هذا التطبيق ""لكل"" المستخدمين؟ ستتم إزالة التطبيق وبياناته من ""كل"" المستخدمين على هذا الجهاز."
+ "هل تريد إزالة هذا التطبيق للمستخدم %1$s؟"
+ "هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات."
+ "هل تريد استبدال هذا التطبيق بإصدار المصنع؟ ستتم إزالة جميع البيانات. وسيؤثر هذا في جميع مستخدمي هذا الجهاز، بما في ذلك من لديهم ملفات شخصية للعمل."
+ "عمليات إلغاء التثبيت الجارية"
+ "عمليات إلغاء التثبيت غير الناجحة"
+ "جارٍ الإزالة..."
+ "جارٍ إلغاء تثبيت %1$s…"
+ "انتهت الإزالة."
+ "تم إلغاء تثبيت %1$s"
+ "تعذّر إلغاء التثبيت."
+ "لم يتم إلغاء تثبيت %1$s بنجاح."
+ "تعذر إلغاء تثبيت تطبيق مشرف الأجهزة النشطة"
+ "تعذر إلغاء تثبيت تطبيق مشرف الأجهزة النشطة لدى %1$s"
+ "هذا التطبيق مطلوب لبعض المستخدمين أو الملفات الشخصية وتم إلغاء تثبيته لآخرين."
+ "هذا التطبيق مطلوب لملفك الشخصي ولا يمكن إلغاء تثبيته."
+ "مشرف الجهاز يحتاج إلى هذا التطبيق ولا يمكن إزالته."
+ "إدارة تطبيقات مشرف الجهاز"
+ "إدارة حسابات المستخدمين"
+ "تعذرت إزالة %1$s."
+ "حدثت مشكلة أثناء تحليل الحزمة."
+ "جديد"
+ "الكل"
+ "الخصوصية"
+ "الدخول إلى الجهاز"
+ "لا يتطلب هذا التحديث أي أذونات جديدة."
+ "رفض"
+ "مزيد من المعلومات"
+ "الرفض على أي حال"
+ "%1$s من %2$s"
+ "هل توافق على منح <b>%1$s</b> إذن %2$s؟"
+ "هل تريد السماح دائمًا للتطبيق <b>%1$s</b> بهذا الإجراء: %2$s؟"
+ "أثناء استخدام التطبيق فقط"
+ "دائمًا"
+ "رفض وعدم طرح السؤال مرة أخرى"
+ "%1$d إذن غير مفعّل"
+ "كل الأذونات غير مفعّلة"
+ "ليس هناك أذونات غير مفعّلة"
+ "سماح"
+ "التطبيقات"
+ "أذونات التطبيق"
+ "عدم السؤال مرة أخرى"
+ "ما مِن أذونات"
+ "أذونات إضافية"
+ "فتح معلومات التطبيق"
+
+ %1$dلا أذونات أخرى
+ إذنان آخران (%1$d)
+ %1$d أذونات أخرى
+ %1$d إذنًا آخر
+ %1$d من الأذونات الأخرى
+ %1$dإذن واحد آخر
+
+ "تم تصميم هذا التطبيق لإصدار قديم من Android. وقد يؤدي رفض الإذن إلى عدم العمل على النحو المطلوب مرة أخرى."
+ "تنفيذ إجراء غير معروف"
+ "تم السماح لـ %1$d من أصل %2$d تطبيق"
+ "عرض النظام"
+ "إخفاء النظام"
+ "ليس هناك أي تطبيقات"
+ "إعدادات الموقع"
+ "يعد %1$s أحد مقدمي خدمات الموقع لهذا الجهاز. يمكن تعديل إمكانية الوصول إلى الموقع من إعدادات الموقع."
+ "في حال رفض هذا الإذن، قد لا تعمل ميزات أساسية في جهازك على النحو المنشود."
+ "فرضته إحدى السياسات"
+ "تمّ إيقاف الوصول إلى الخلفية بواسطة السياسة."
+ "تمّ تفعيل الوصول إلى الخلفية بواسطة السياسة."
+ "تمّ تفعيل الوصول إلى المقدمة بواسطة السياسة."
+ "إعدادات يتحكم فيها المشرف"
+
+
+
+ "دائمًا"
+ "أثناء استخدام التطبيق فقط"
+ "أبدًا"
+ "جارٍ التحميل..."
+ "كل الأذونات"
+ "إمكانات التطبيق الأخرى"
+ "طلب الإذن"
+ "تم اكتشاف طبقة متراكبة للشاشة"
+ "لتغيير إعداد هذا الإذن، يتعين عليك أولاً إيقاف الطبقة المتراكبة للشاشة من الإعدادات > التطبيقات"
+ "فتح الإعدادات"
+ "Android Wear"
+ "لا تتوافق إجراءات التثبيت/إلغاء التثبيت مع نظام Android Wear."
+ "اختيار ما تريد السماح لتطبيق <b>%1$s</b> بالوصول إليه"
+ "تم تحديث <b>%1$s</b>. عليك اختيار ما تريد السماح لهذا التطبيق بالوصول إليه."
+ "إلغاء"
+ "متابعة"
+ "الأذونات الجديدة"
+ "الأذونات الحالية"
+ "جارٍ الطرح المرحلي للتطبيق…"
+ "غير معروف"
+ "لأغراض الأمان، غير مسموح لجهازك اللوحي بتثبيت تطبيقات غير معروفة من هذا المصدر."
+ "لأغراض الأمان، غير مسموح لجهاز التلفزيون الذي تستخدمه بتثبيت تطبيقات غير معروفة من هذا المصدر."
+ "لأغراض الأمان، غير مسموح لهاتفك بتثبيت تطبيقات غير معروفة من هذا المصدر."
+ "يعتبر الهاتف والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث لهاتفك أو فقدان البيانات الذي قد ينتج عن استخدامه."
+ "يعتبر الجهاز اللوحي والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث للجهاز اللوحي أو فقدان البيانات الذي قد ينتج عن استخدامه."
+ "يعتبر جهاز التلفزيون والبيانات الشخصية أكثر عرضة لهجوم التطبيقات غير المعروفة. من خلال تثبيت هذا التطبيق، توافق على تحمل مسؤولية أي ضرر يحدث لجهاز التلفزيون أو فقدان البيانات الذي قد ينتج عن استخدامه."
+ "متابعة"
+ "الإعدادات"
+ "تثبيت / إلغاء تثبيت تطبيقات Android Wear"
+
diff --git a/packages/PackageInstaller/res/values-as-television/strings.xml b/packages/PackageInstaller/res/values-as-television/strings.xml
new file mode 100644
index 0000000000000..2aaae1e9efd6a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"
+ "আপুনি ইয়াক পিছত ছেটিংসমূহ > এপসমূহ-লৈ গৈ সলনি কৰিব পাৰিব"
+ "%1$s / %2$s"
+ "ছিষ্টেম এপসমূহ দেখুৱাওক"
+ "এপৰ অনুমতিসমূহ"
+ "এপৰ অনুমতিসমূহ"
+ "%1$s অনুমতিসমূহ"
+ "অতিৰিক্ত অনুমতিসমূহ"
+ "%1$s অনুমতিসমূহ"
+
diff --git a/packages/PackageInstaller/res/values-as-watch/strings.xml b/packages/PackageInstaller/res/values-as-watch/strings.xml
new file mode 100644
index 0000000000000..3889fc4871c36
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"
+ "%1$s / %2$s"
+ "ছিষ্টেম এপসমূহ দেখুৱাওক"
+ "সলনি কৰিব নোৱাৰি"
+ "হয়"
+ "বাতিল কৰক"
+
diff --git a/packages/PackageInstaller/res/values-as/strings.xml b/packages/PackageInstaller/res/values-as/strings.xml
new file mode 100644
index 0000000000000..de2f2d679e2b8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-as/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "পেকেজ ইনষ্টলাৰ"
+ "পৰৱৰ্তী"
+ "ইনষ্টল কৰক"
+ "সম্পন্ন হ\'ল"
+ "বাতিল কৰক"
+ "ইনষ্টল কৰি থকা হৈছে…"
+ "%1$s ইনষ্টল কৰি থকা হৈছে…"
+ "এপ্ ইনষ্টল কৰা হ\'ল।"
+ "আপুনি এই এপ্লিকেশ্বন ইনষ্টল কৰিব বিচাৰেনে? ই এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"
+ "আপুনি এই এপ্লিকেশ্বন ইনষ্টল কৰিব বিচাৰেনে? ইয়াক ব্য়ৱহাৰ সম্পৰ্কীয় কোনো অনুমতিৰ প্ৰয়োজন নাই।"
+ "আপুনি পূর্বৰে পৰা থকা এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাই। আপডেট হোৱা এপ্লিকেশ্বনে এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"
+ "আপুনি এই অন্তনির্মিত এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাই। আপডেট হোৱা এপ্লিকেশ্বনে এইবোৰ ব্যৱহাৰ কৰিব পাৰিব:"
+ "আপুনি পূর্বৰে পৰা থকা এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি কোনো পুৰণি ডেটা নেহেৰুৱাব। ব্যৱহাৰৰ বাবে ইয়াক কোনো বিশেষ অনুমতিৰ প্ৰয়োজন নাই৷"
+ "আপুনি এই অন্তনির্মিত এপ্লিকেশ্বন আপডেট কৰিব বিচাৰেনে? আপুনি নিজৰ পুৰণি ডেটা নেহেৰুৱাব৷ ব্যৱহাৰৰ বাবে ইয়াক কোনো বিশেষ অনুমতিৰ প্ৰয়োজন নাই৷"
+ "এপ্ ইনষ্টল কৰা হোৱা নাই।"
+ "পেকেজটোৰ ইনষ্টল অৱৰোধ কৰা হৈছে।"
+ "এপটো ইনষ্টল কৰিব পৰা নহ\'ল কাৰণ ইয়াৰ পেকেজ আৰু পূর্বৰে পৰা উপলব্ধ পেকেজৰ মাজত সমস্যাৰ সৃষ্টি হৈছে।"
+ "আপোনাৰ টেবলেটৰ সৈতে মিল নথকাৰ বাবে এপটো ইনষ্টল নহ\'ল।"
+ "আপোনাৰ টিভিত এই এপ্ চলিব নোৱাৰে।"
+ "আপোনাৰ ফ\'নৰ সৈতে মিল নথকাৰ বাবে এপটো ইনষ্টল নহ\'ল।"
+ "পেকেজ মান্য নোহোৱাৰ বাবে এপটো ইনষ্টল নহ\'ল।"
+ "%1$sক আপোনাৰ টে\'বলেটত ইনষ্টল কৰিব পৰা নগ\'ল৷"
+ "আপোনাৰ টিভিত %1$s ইনষ্টল কৰিব পৰা নগ\'ল।"
+ "%1$sক আপোনাৰ ফ\'নত ইনষ্টল কৰিব পৰা নগ\'ল৷"
+ "খোলক"
+ "আপোনাৰ প্ৰশাসকে অজ্ঞাত উৎসৰ পৰা লাভ কৰা এপ্ ইনষ্টল কৰাৰ অনুমতি দিয়া নাই"
+ "এই ব্যৱহাৰকাৰীয়ে অজ্ঞাত উৎসৰপৰা লাভ কৰা এপসমূহ ইনষ্টল কৰিব নোৱাৰে"
+ "এই ব্যৱহাৰকাৰীক এপ্ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই"
+ "ঠিক আছে"
+ "এপসমূহ পৰিচালনা কৰক"
+ "পৰ্যাপ্ত খালী ঠাই নাই"
+ "%1$sক ইনষ্টল কৰিব পৰা নগ\'ল। কিছু খালী ঠাই উলিয়াই পুনৰ চেষ্টা কৰক৷"
+ "এপ্ পোৱা নগ\'ল"
+ "ইনষ্টল হৈ থকা এপসমূহৰ তালিকাত এই এপটো পোৱা নগ\'ল।"
+ "অনুমতি দিয়া হোৱা নাই"
+ "বর্তমানৰ ব্যৱহাৰকাৰীক আনইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"
+ "আসোঁৱাহ"
+ "এপ্ আনইনষ্টল কৰিব পৰা নাযাব।"
+ "এপ্ আনইনষ্টল কৰক"
+ "আপডেট আনইনষ্টল কৰক"
+ "%1$s হৈছে তলৰ এপটোৰ এটা অংশ:"
+ "আপুনি এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"
+ "আপুনি ""সকলো"" ব্যৱহাৰকাৰীৰ বাবে এই এপটো আনইনষ্টল কৰিবলৈ বিচাৰেনে? এপ্লিকেশ্বন আৰু ইয়াৰ ডেটাক ডিভাইচটোত থকা ""সকলো"" ব্যৱহাৰকাৰীৰ পৰা আঁতৰোৱা হ\'ব৷"
+ "আপুনি ব্যৱহাৰকাৰী %1$sৰ বাবে এই এপটো আনইনষ্টল কৰিব বিচাৰেনে?"
+ "এই এপটো ফেক্টৰী সংস্কৰণৰ সৈতে সলনি কৰিব বিচাৰেনে? সকলো তথ্য় মচা হ\'ব।"
+ "এই এপটো ফেক্টৰী সংস্কৰণৰ সৈতে সলনি কৰিব বিচাৰেনে? সকলো তথ্য় মচা হ\'ব। ইয়াৰ প্ৰভাৱ কার্মস্থানৰ প্ৰফাইল থকা ডিভাইচটোৰ ব্য়ৱহাৰকাৰীসকলৰ লগতে অইন সকলো ব্য়ৱহাৰকাৰীৰ ওপৰতো পৰিব।"
+ "আনইনষ্টল হৈ থকা বস্তুবোৰ"
+ "আনইনষ্টল কৰিব নোৱাৰা বস্তুবোৰ"
+ "আনইনষ্টল কৰি থকা হৈছে…"
+ "%1$sক আনইনষ্টল কৰি থকা হৈছে…"
+ "আনইনষ্টল কাৰ্যটো সমাপ্ত হ\'ল৷"
+ "%1$s আনইনষ্টল কৰা হ\'ল"
+ "আনইনষ্টল কৰিব পৰা নগ\'ল।"
+ "%1$sক আনইনষ্টল কৰিব পৰা নগ\'ল।"
+ "ডিভাইচৰ সক্ৰিয় প্ৰশাসক এপ্ আনইনষ্টল কৰিব নোৱাৰি"
+ "%1$sৰ সক্ৰিয় ডিভাইচৰ প্ৰশাসকীয় এপ্ আনইনষ্টল কৰিব নোৱাৰি"
+ "এই এপটো কিছুসংখ্য়ক ব্যৱহাৰকাৰী বা প্ৰ\'ফাইলৰ বাবে প্ৰয়োজনীয় আৰু বাকীসকলৰ বাবে ইয়াক আনইনষ্টল কৰা হৈছে"
+ "আপোনাৰ প্ৰ\'ফাইলৰ বাবে এই এপৰ প্ৰয়োজন আছে গতিকে আনইনষ্টল কৰিব পৰা নাযায়।"
+ "আপোনাৰ ডিভাইচৰ প্ৰশাসকে এই এপটো ৰখাটো বাধ্যতামূলক কৰি ৰাখিছে, গতিকে ইয়াক আনইনষ্টল কৰিব পৰা নাযায়।"
+ "ডিভাইচৰ প্ৰশাসকীয় এপসমূহ পৰিচালনা কৰক"
+ "ব্য়ৱহাৰকাৰীসকলক পৰিচালনা কৰক"
+ "%1$sক আনইনষ্টল কৰিব নোৱাৰি৷"
+ "পেকেজটো পাৰ্ছ কৰোঁতে এটা সমস্যাই দেখা দিছিল।"
+ "নতুন"
+ "সকলো"
+ "গোপনীয়তা"
+ "ডিভাইচৰ ব্যৱহাৰ"
+ "এই আপডেটক কোনো নতুন অনুমতিৰ প্ৰয়োজন নাই।"
+ "প্ৰত্যাখ্যান কৰক"
+ "অধিক তথ্য"
+ "যিহ\'লেও অস্বীকাৰ কৰক"
+ "%2$sৰ ভিতৰত%1$sটা"
+ "<b>%1$s</b>ক %2$sৰ বাবে অনুমতি দিবনে?"
+ "<b>%1$s</b>ক সদায় %2$s কৰাৰ অনুমতি দিবনে?"
+ "এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে মাত্ৰ"
+ "সদায়"
+ "অস্বীকাৰ কৰক আৰু পুনৰাই নুসুধিব"
+ "%1$dটা অক্ষম কৰা হ\'ল"
+ "সকলো অক্ষম কৰা হ\'ল"
+ "একো অক্ষম কৰা হোৱা নাই"
+ "অনুমতি দিয়ক"
+ "এপসমূহ"
+ "এপক দিয়া অনুমতিসমূহ"
+ "পুনৰাই নুসুধিব"
+ "কোনো অনুমতি নাই"
+ "অতিৰিক্ত অনুমতিসমূহ"
+ "এপৰ তথ্য় খোলক"
+
+ %1$d অধিক
+ %1$d অধিক
+
+ "এই এপটো Androidৰ এটা পুৰণা সংস্কৰণৰ বাবে প্ৰস্তুত কৰা হৈছিল। অনুমতি নিদিলে ই বিচৰাধৰণে কাম নকৰিবও পাৰে।"
+ "অজ্ঞাত কাৰ্য কৰিব পাৰে"
+ "%2$dৰ ভিতৰত%1$dটা এপক অনুমতি দিয়া হৈছে"
+ "ছিষ্টেম দেখুৱাওক"
+ "ছিষ্টেম লুকুৱাওক"
+ "কোনো এপে এই অনুমতি বিচৰা নাই"
+ "অৱস্থান ছেটিংসমূহ"
+ "%1$s এই ডিভাইচৰ অৱস্থান সেৱা প্ৰদানকাৰী। অৱস্থানৰ ছেটিংসমূহত অৱস্থানৰ ব্যৱহাৰ সংশোধন কৰিব পাৰি।"
+ "আপুনি যদি এই অনুমতি প্ৰদান নকৰে, তেন্তে আপোনাৰ ডিভাইচৰ মৌলিক সুবিধাসমূহে বিচৰাধৰণে কাম নকৰিবও পাৰে।"
+ "নীতিৰ যোগেদি বলৱৎ কৰা"
+ "নীতি অনুসৰি নেপথ্য় চোৱা সুবিধা অক্ষম কৰা হ’ল"
+ "নীতি অনুসৰি নেপথ্য় চোৱা সুবিধা সক্ষম কৰা হ’ল"
+ "নীতি অনুসৰি অগ্ৰভূমি চোৱা সুবিধা সক্ষম কৰা হ’ল"
+ "প্ৰশাসকে নিয়ন্ত্ৰিত কৰা"
+
+
+
+ "সদায়"
+ "এপ্ ব্য়ৱহাৰ কৰি থাকোঁতে মাত্ৰ"
+ "কেতিয়াও নহয়"
+ "ল\'ড কৰি থকা হৈছে…"
+ "সকলো অনুমতি"
+ "অন্য এপৰ কার্যক্ষমতাসমূহ"
+ "অনুমতি বিচাৰি কৰা অনুৰোধ"
+ "স্ক্ৰীণ অভাৰলে\' চিনাক্ত কৰা হৈছে"
+ "এই অনুমতিৰ ছেটিং সলনি কৰিবলৈ আপুনি প্ৰথমে ছেটিংসমূহ > এপসমূহ-লৈ গৈ স্ক্ৰীণ অভাৰলে\' অফ কৰিব লাগিব।"
+ "ছেটিংসমূহ খোলক"
+ "Android ৱেৰ"
+ "ইনষ্টল/আনইনষ্টল কাৰ্য Wearত কৰিব নোৱাৰি।"
+ "<b>%1$s</b>ক কি কিত প্ৰৱেশ কৰিবলৈ অনুমতি দিব বাছনি কৰক"
+ "<b>%1$s</b> আপডেট কৰা হৈছে। এই এপক কি কিত প্ৰৱেশ কৰিবলৈ অনুমতি দিব বাছনি কৰক।"
+ "বাতিল কৰক"
+ "অব্যাহত ৰাখক"
+ "নতুন অনুমতিসমূহ"
+ "এপে বর্তমান ব্যৱহাৰ কৰি থকা অনুমতিসমূহ"
+ "এপৰ অন্তিম পর্যায়ৰ পৰীক্ষণ চলি আছে…"
+ "অজ্ঞাত"
+ "আপোনাৰ টেবলেটটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ টেবলেটটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"
+ "আপোনাৰ টিভিটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ টিভিটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"
+ "আপোনাৰ ফ\'নটো যাতে সুৰক্ষিত থাকে তাৰ বাবে আপোনাৰ ফ\'নটোক এই উৎসৰ পৰা অজ্ঞাত এপসমূহ ইনষ্টল কৰিবলৈ অনুমতি দিয়া হোৱা নাই।"
+ "আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ ফ\'নত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"
+ "আপোনাৰ টেবলেট আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ টেবলেটত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"
+ "আপোনাৰ টিভি আৰু ব্যক্তিগত ডেটা অজ্ঞাত এপৰ আক্ৰমণৰ বলি হোৱাৰ সম্ভাৱনা অধিক। এই এপটো ইনষ্টল কৰি আপুনি ইয়াক ব্যৱহাৰ কৰাৰ ফলত আপোনাৰ টিভিত কোনো ক্ষতি হ\'লে বা ডেটা হেৰুৱালে আপুনিয়েই দায়ী হ\'ব বুলি সন্মত।"
+ "অব্যাহত ৰাখক"
+ "ছেটিংবোৰ"
+ "ৱেৰ এপসমূহ ইনষ্টল/আনইনষ্টল কৰি থকা হৈছে"
+
diff --git a/packages/PackageInstaller/res/values-az-television/strings.xml b/packages/PackageInstaller/res/values-az-television/strings.xml
new file mode 100644
index 0000000000000..92fa52705e81c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Rədd edin və daha soruşmayın"
+ "Bunu sonra Ayarlar vəTətbiqlər bölməsindən dəyişə bilərsiniz"
+ "%1$s / %2$s"
+ "Sistem tətbiqlərini göstərin"
+ "Tətbiq icazələri"
+ "Tətbiq icazələri"
+ "%1$s icazələri"
+ "Əlavə icazələr"
+ "%1$s icazələri"
+
diff --git a/packages/PackageInstaller/res/values-az-watch/strings.xml b/packages/PackageInstaller/res/values-az-watch/strings.xml
new file mode 100644
index 0000000000000..ef6723b5b508c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Rədd edin, bir daha soruşmayın"
+ "%1$s / %2$s"
+ "Sistem tətbiqlərini göstərin"
+ "Dəyişdirilə bilməz"
+ "Bəli"
+ "Ləğv edin"
+
diff --git a/packages/PackageInstaller/res/values-az/strings.xml b/packages/PackageInstaller/res/values-az/strings.xml
new file mode 100644
index 0000000000000..d8708879ee65a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-az/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paket quraşdırıcı"
+ "Növbəti"
+ "Quraşdır"
+ "Hazırdır"
+ "Ləğv et"
+ "Quraşdırılır..."
+ "%1$s quraşdırılır…"
+ "Tətbiq quraşdırılıb."
+ "Bu tətbiqi quraşdırmaq istəyirsiniz? Tətbiq buraya giriş əldə edəcək:"
+ "Bu tətbiqi quraşdırmaq istəyirsiniz? Hər hansı bir xüsusi keçid tələb etmir."
+ "Bu cari tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. Güncəllənmiş tətbiq aşağıdakılara çıxış əldə edəcək:"
+ "Daxili tətbiqdən yenilənməni quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. Yenilənmiş tətbiq aşağıdakılara çıxış əldə edəcək:"
+ "Bu cari tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. O, xüsusi giriş tələb etmir."
+ "Bu daxili tətbiq güncəllənməsini quraşdırmaq istəyirsiniz? Hazırki datanız itməyəcək. O, xüsusi giriş tələb etmir."
+ "Tətbiq quraşdırılmayıb."
+ "Paket yüklənməyə qarşı blok edildi."
+ "Bu paketin mövcud paket ilə ziddiyəti səbəbiylə tətbiq quraşdırılmadı."
+ "Bu tətbiq planşetinizə uyğun gəlmədiyi üçün tətbiq quraşdırılmadı."
+ "Bu proqram TV-nizlə uyğun gəlmir."
+ "Bu tətbiq telefonunuza uyğun gəlmədiyi üçün tətbiq quraşdırılmadı."
+ "Paket yanlış kimi göründüyü üçün tətbiq quraşdırılmadı."
+ "%1$s planşetinizə yüklənə bilmədi."
+ "%1$s proqramını TV-nizdə quraşdırmaq mümkün olmadı."
+ "%1$s telefonunuza quraşdırıla bilmədi."
+ "Aç"
+ "Naməlum mənbələrdən əldə edilmiş tətbiqlərin quraşdırılmasına admin tərəfindən icazə verilmir"
+ "Naməlum tətbiqlər bu istifadəçi tərəfindən quraşdırıla bilməz"
+ "Bu istifadəçinin tətbiqi quraşdırmaq üçün icazəsi yoxdur"
+ "OK"
+ "Tətbiqləri idarə et"
+ "Boş yer yoxdur"
+ "%1$s quraşdırıla bilməz. Yaddaş üçün yer boşaldıb yenidən təkrar edin."
+ "Tətbiq tapılmadı"
+ "Tətbiq quraşdırılmış tətbiqlər siyahısında tapılmadı."
+ "İcazə verilmir"
+ "Cari istifadəçiyə bu silinməni həyata keçirməyə icazə verilmir."
+ "Xəta"
+ "Tətbiq sistemdən silinmədi."
+ "Tətbiqi qaldır"
+ "Güncəlləməni sil"
+ "%1$s bu tətbiqin hissəsidir:"
+ "Bu tətbiqi aradan qaldırmaq istəyirsiniz mi?"
+ "Bu tətbiqi ""bütün"" istifadəçilər üçün silmək istəyirsiz? Tətbiq və onun datası cihazdakı ""bütün"" istifadəçilər üçün silinəcək."
+ "%1$s adlı istifadəçi üçün bu tətbiqi sistemdən silmək istəyirsiniz?"
+ "Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək."
+ "Tətbiq zavod versiyası ilə əvəz olunsun? Bütün data silinəcək. Bu, iş profilləri olanlar da daxil olmaqla bu cihazın bütün istifadəçilərinə təsir edir."
+ "İşləyən sistemlər silinmələr"
+ "Uğursuz olan sistemlər silinmələr"
+ "Silinir..."
+ "%1$s sistemdən silinir…"
+ "Sistemdən silmə tamamlandı."
+ "%1$s sistemdən silindi"
+ "Aradan qaldırılma uğursuz oldu."
+ "%1$s sistemdən silinməsi uğursuz oldu."
+ "Aktiv cihaz admin tətbiqini sistemdən silmək mümkün olmadı"
+ "%1$s üçün aktiv cihaz admin tətbiqini sistemdən silmək mümkün olmadı"
+ "Bu tətbiq bəzi istifadəçi və profillər tərəfindən tələb olunur və digərləri üçün silinib"
+ "Bu tətbiq profil üçün tələb olunur və silinə bilməz."
+ "Bu tətbiq cihaz administratoru tərəfindən tələb olunur və sistemdən silinə bilməz."
+ "Cihaz admin tətbiqlərini idarə edin"
+ "İstifadəçiləri idarə edin"
+ "%1$s sistemdən silinə bilməz."
+ "Paketin təhlilində problem var idi."
+ "Yeni"
+ "Hamısı"
+ "Məxfilik"
+ "Qurğu icazəsi"
+ "Bu güncəllənmə heç bir icazə istəmir"
+ "Rədd edin"
+ "Daha ətraflı"
+ "Hər bir halda rədd edin"
+ "%2$s icazədən %1$s ədəd"
+ "<b>%1$s</b> tətbiqinə %2$s fəaliyyəti üçün icazə verilsin?"
+ "<b>%1$s</b> tətbiqinin %2$s əməliyyatına daima icazə verilsin?"
+ "Ancaq tətbiq istifadəsi zamanı"
+ "Həmişə"
+ "Rədd edin və daha soruşmayın"
+ "%1$d deaktiv edildi"
+ "hamısı deaktiv edildi"
+ "heç biri deaktiv edilmədi"
+ "İcazə verin"
+ "Tətbiqlər"
+ "Tətbiq icazələri"
+ "Bir daha soruşmayın"
+ "İcazə yoxdur"
+ "Əlavə icazələr"
+ "Tətbiq məlumatını açın"
+
+ daha %1$d
+ daha %1$d
+
+ "Bu tətbiq köhnə Android versiyası üçün nəzərdə tutulub. İcazəni rədd etmək onun lazımi şəkildə işləməməsinə səbəb ola bilər."
+ "naməlum əməliyyat etmək"
+ "%2$d tətbiqdən %1$d ədədinə icazə var"
+ "Sistemi göstərin"
+ "Sistemi gizlədin"
+ "Tətbiq yoxdur"
+ "Məkan Ayarları"
+ "%1$s bu cihaz üçün məkan xidmətləri təminatçısıdır. Məkana giriş məkan ayarlarından dəyişdirilə bilər."
+ "Bu icazəni rədd etsəniz, cihazınızın əsas funksiyaları lazımi qaydada işləməyə bilər."
+ "Siyasət tərəfindən tətbiq olunur"
+ "Arxa fon girişi siyasətə əsasən deaktiv edildi"
+ "Arxa fon girişi siyasətə əsasən aktiv edildi"
+ "Ön fon girişi siyasətə əsasən aktiv edildi"
+ "Admin tərəfindən nəzarət olunur"
+
+
+
+ "Həmişə"
+ "Ancaq tətbiq istifadəsi zamanı"
+ "Heç vaxt"
+ "Yüklənir…"
+ "Bütün icazələr"
+ "Digər tətbiq imkanları"
+ "İcazə sorğusu"
+ "Ekran örtüyü aşkarlandı"
+ "Bu icazə ayarını dəyişdirmək üçün əvvəldə Ayarlar və Tətbiqlər bölməsindən ekran örtüyünü söndürməlisiniz"
+ "Ayarları açın"
+ "Android Wear"
+ "Yükləmə/Silmə fəaliyyətləri Wear\'də dəstəklənmir."
+ "<b>%1$s</b> tətbiqinin giriş hüququnu seçin"
+ "<b>%1$s</b> tətbiqi güncəlləndi. Bu tətbiqin giriş hüququnu seçin."
+ "Ləğv edin"
+ "Davam edin"
+ "Yeni icazələr"
+ "Cari icazələr"
+ "Tətbiq hazırlanır..."
+ "Naməlum"
+ "Təhlükəsizliyiniz üçün planşetə bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."
+ "Təhlükəsizliyiniz üçün TV-yə bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."
+ "Təhlükəsizliyiniz üçün telefona bu mənbədən olan naməlum tətbiqləri quraşdırmağa icazə verilmir."
+ "Telefon və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla telefona dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verən data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."
+ "Planşet və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla planşetə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verə biləcək data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."
+ "Tv və şəxsi data naməlum tətbiqlərin hücumuna qarşı daha həssasdır. Bu tətbiqi quraşdırmaqla Tv\'ə dəyə biləcək zərər və ya onun istifadəsi nəticəsində baş verən data itkisinə görə məsuliyyət daşıdığınızı qəbul edirsiniz."
+ "Davam edin"
+ "Ayarlar"
+ "Wear tətbiqləri quraşdırılır/silinir"
+
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml
new file mode 100644
index 0000000000000..5dce759d6e544
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Odbij i ne pitaj ponovo"
+ "Ovo možete da promenite kasnije u Podešavanjima > Aplikacije"
+ "%1$s/%2$s"
+ "Prikaži sistemske aplikacije"
+ "Dozvole za aplikacije"
+ "Dozvole za aplikacije"
+ "Dozvole za aplikaciju %1$s"
+ "Dodatne dozvole"
+ "Dozvole za aplikaciju %1$s"
+
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml
new file mode 100644
index 0000000000000..63a44db95bdab
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Odbij i ne pitaj ponovo"
+ "%1$s/%2$s"
+ "Prikaži sistemske aplikacije"
+ "Ne može da se promeni"
+ "Da"
+ "Otkaži"
+
diff --git a/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
new file mode 100644
index 0000000000000..4d8772fc9d4a5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-b+sr+Latn/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Upakovani program za instalaciju"
+ "Dalje"
+ "Instaliraj"
+ "Gotovo"
+ "Otkaži"
+ "Instaliranje..."
+ "Instalira se %1$s…"
+ "Aplikacija je instalirana."
+ "Želite li da instalirate ovu aplikaciju? Imaće pristup sledećem:"
+ "Želite li da instalirate ovu aplikaciju? Ne zahteva poseban pristup."
+ "Želite li da instalirate ažuriranje za ovu postojeću aplikaciju? Postojeći podaci neće biti izgubljeni. Ažurirana aplikacija imaće pristup sledećem:"
+ "Želite li da instalirate ažuriranje za ovu ugrađenu aplikaciju? Postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će imati pristup sledećem:"
+ "Da li želite da instalirate ažuriranje ove postojeće aplikacije? Postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."
+ "Da li želite da instalirate ažuriranje ove ugrađene aplikacije? Postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."
+ "Aplikacija nije instalirana."
+ "Instaliranje paketa je blokirano."
+ "Aplikacija nije instalirana jer je paket neusaglašen sa postojećim paketom."
+ "Aplikacija nije instalirana jer nije kompatibilna sa tabletom."
+ "Ova aplikacija nije kompatibilna sa TV-om."
+ "Aplikacija nije instalirana jer nije kompatibilna sa telefonom."
+ "Aplikacija nije instalirana jer je paket nevažeći."
+ "Nije moguće instalirati aplikaciju %1$s na tablet."
+ "Nismo uspeli da instaliramo %1$s na TV."
+ "Nije moguće instalirati aplikaciju %1$s na telefon."
+ "Otvori"
+ "Administrator ne dozvoljava instaliranje aplikacija dobijenih iz nepoznatih izvora"
+ "Ovaj korisnik ne može da instalira nepoznate aplikacije"
+ "Ovom korisniku nije dozvoljeno da instalira aplikacije"
+ "Potvrdi"
+ "Upravljanje aplikacijama"
+ "Nema više mesta"
+ "Nije moguće instalirati aplikaciju %1$s. Oslobodite dodatni prostor i pokušajte ponovo."
+ "Aplikacija nije pronađena"
+ "Aplikacija nije pronađena na listi instaliranih aplikacija."
+ "Nije dozvoljeno"
+ "Aktuelnom korisniku nije dozvoljeno da obavi ovo deinstaliranje."
+ "Greška"
+ "Deinstaliranje aplikacije nije uspelo."
+ "Deinstaliranje aplikacije"
+ "Deinstaliranje ažuriranja"
+ "%1$s je deo sledeće aplikacije:"
+ "Da li želite da deinstalirate ovu aplikaciju?"
+ "Da li želite da deinstalirate ovu aplikaciju za ""sve"" korisnike? Aplikacija i podaci koji se na nju odnose biće uklonjeni za ""sve"" korisnike ovog uređaja."
+ "Želite li da deinstalirate ovu aplikaciju za korisnika %1$s?"
+ "Želite li da zamenite ovu aplikaciju fabričkom verzijom? Svi podaci će biti uklonjeni."
+ "Želite li da zamenite ovu aplikaciju fabričkom verzijom? Svi podaci će biti uklonjeni. Ovo utiče na sve korisnike ovog uređaja, uključujući i one sa profilima za Work."
+ "Aktivna deinstaliranja"
+ "Neuspela deinstaliranja"
+ "Deinstaliranje..."
+ "%1$s se deinstalira…"
+ "Deinstaliranje je završeno."
+ "Aplikacija %1$s je deinstalirana"
+ "Deinstaliranje nije uspelo."
+ "Deinstaliranje aplikacije %1$s nije uspelo."
+ "Ne možete da deinstalirate aplikaciju za aktivnog administratora uređaja"
+ "Ne možete da deinstalirate aplikaciju za aktivnog administratora uređaja za %1$s"
+ "Ova aplikacija je potrebna za neke korisnike ili profile, a deinstalirana je za druge"
+ "Ova aplikacija je potrebna za vaš profil i ne može da se deinstalira."
+ "Ova aplikacija je potrebna administratoru uređaja i ne može da se deinstalira."
+ "Upravljaj aplikacijama za administratore uređaja"
+ "Upravljaj korisnicima"
+ "Nije moguće deinstalirati aplikaciju %1$s."
+ "Došlo je do problema pri raščlanjivanju paketa."
+ "Novo"
+ "Sve"
+ "Privatnost"
+ "Pristup uređaju"
+ "Ovo ažuriranje ne zahteva nove dozvole."
+ "Odbaci"
+ "Više informacija"
+ "Ipak odbij"
+ "%1$s. od %2$s"
+ "Želite li da dozvolite da <b>%1$s</b> %2$s?"
+ "Želite li uvek da dozvolite da <b>%1$s</b> %2$s?"
+ "Samo dok se aplikacija koristi"
+ "Uvek"
+ "Odbij i ne pitaj ponovo"
+ "Onemogućenih: %1$d"
+ "sve su onemogućene"
+ "nijedna nije onemogućena"
+ "Dozvoli"
+ "Aplikacije"
+ "Dozvole za aplikacije"
+ "Ne pitaj ponovo"
+ "Nema dozvola"
+ "Dodatne dozvole"
+ "Otvori informacije o aplikaciji"
+
+ još %1$d
+ još %1$d
+ još %1$d
+
+ "Ova aplikacija je dizajnirana za stariju verziju Android-a. Ako odbijete dozvolu, ona možda više neće pravilno da funkcioniše."
+ "obavlja nepoznatu radnju"
+ "%1$d od %2$d aplikacija ima dozvolu"
+ "Prikaži sistemske"
+ "Sakrij sistemske"
+ "Nema aplikacija"
+ "Podešavanja lokacije"
+ "%1$s pruža usluge lokacije za ovaj uređaj. Pristup lokaciji možete da izmenite u podešavanjima lokacije."
+ "Ako odbijete ovu dozvolu, osnovne funkcije uređaja možda neće više funkcionisati ispravno."
+ "Primenjuje se u skladu sa smernicama"
+ "Pristup u pozadini je onemogućen smernicama"
+ "Pristup u pozadini je omogućen smernicama"
+ "Pristup u prvom planu je omogućen smernicama"
+ "Kontroliše administrator"
+
+
+
+ "Uvek"
+ "Samo dok se aplikacija koristi"
+ "Nikada"
+ "Učitava se…"
+ "Sve dozvole"
+ "Ostale mogućnosti aplikacije"
+ "Zahtev za dozvolu"
+ "Otkriven je element koji prekriva sadržaj ekrana"
+ "Da biste promenili podešavanje ove dozvole, prvo treba da isključite element koji prekriva sadržaj ekrana u odeljku Podešavanja > Aplikacije"
+ "Otvori podešavanja"
+ "Android Wear"
+ "Radnje Instaliraj/Deinstaliraj nisu podržane u Wear-u."
+ "Izaberite čemu <b>%1$s</b> može da pristupa"
+ "Aplikacija <b>%1$s</b> je ažurirana. Izaberite čemu ova aplikacija može da pristupa."
+ "Otkaži"
+ "Nastavi"
+ "Nove dozvole"
+ "Aktuelne dozvole"
+ "Aplikacija se priprema…"
+ "Nepoznato"
+ "Tabletu iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."
+ "Televizoru iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."
+ "Telefonu iz bezbednosnih razloga nije dozvoljeno da instalira nepoznate aplikacije iz ovog izvora."
+ "Telefon i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja telefona ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."
+ "Tablet i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja tableta ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."
+ "TV i lični podaci su podložniji napadu nepoznatih aplikacija. Ako instalirate ovu aplikaciju, prihvatate da ste odgovorni za eventualna oštećenja TV-a ili gubitak podataka do kojih može da dođe zbog njenog korišćenja."
+ "Nastavi"
+ "Podešavanja"
+ "Instaliranje/deinstaliranje Wear aplikacija"
+
diff --git a/packages/PackageInstaller/res/values-be-television/strings.xml b/packages/PackageInstaller/res/values-be-television/strings.xml
new file mode 100644
index 0000000000000..befb367c99546
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Адхіліць і больш не пытацца"
+ "Пазней гэта можна змянiць у раздзеле «Налады > Праграмы»"
+ "%1$s / %2$s"
+ "Паказваць сістэмныя праграмы"
+ "Дазволы праграм"
+ "Дазволы праграм"
+ "Дазволы праграмы %1$s"
+ "Дадатковыя дазволы"
+ "Дазволы праграмы %1$s"
+
diff --git a/packages/PackageInstaller/res/values-be-watch/strings.xml b/packages/PackageInstaller/res/values-be-watch/strings.xml
new file mode 100644
index 0000000000000..99b2ce8a95e2a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Адхіліць, больш не пытацца"
+ "%1$s / %2$s"
+ "Паказваць сістэмныя праграмы"
+ "Нельга змяніць"
+ "Так"
+ "Скасаваць"
+
diff --git a/packages/PackageInstaller/res/values-be/strings.xml b/packages/PackageInstaller/res/values-be/strings.xml
new file mode 100644
index 0000000000000..1b65e29e6d7bf
--- /dev/null
+++ b/packages/PackageInstaller/res/values-be/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Усталёўшчык пакетаў"
+ "Далей"
+ "Усталяваць"
+ "Гатова"
+ "Скасаваць"
+ "Усталяванне..."
+ "Ідзе ўсталяванне %1$s…"
+ "Прыкладанне ўсталявана."
+ "Усталяваць гэта прыкладанне? Яно атрымае доступ да:"
+ "Усталяваць гэта прыкладанне? Яно не патрабуе спецыяльнага доступу."
+ "Усталяваць абнаўленне для гэтага існуючага прыкладання? Існуючыя дадзеныя не будуць страчаны. Абноўленае прыкладанне атрымае доступ да:"
+ "Усталяваць абнаўленне для гэтага ўбудаванага прыкладання? Існуючыя дадзеныя не будуць страчаны. Абноўленае прыкладанне атрымае доступ да:"
+ "Усталяваць абнаўленне для гэтага прыкладання? Вашы iснуючыя дадзеныя не будуць згублены. Спецыяльны доступ не патрабуецца."
+ "Усталяваць абнаўленне для гэтага ўбудаванага прыкладання? Вашы iснуючыя дадзеныя не будуць згублены. Спецыяльны доступ не патрабуецца."
+ "Прыкладанне не ўсталявана."
+ "Для пакета заблакіравана магчымасць усталявання."
+ "Праграма не ўсталявана, таму што пакет канфліктуе з існуючым пакетам."
+ "Праграма не ўсталявана, таму што яна несумяшчальная з вашым планшэтам."
+ "Гэта праграма несумяшчальная з вашым тэлевізарам."
+ "Праграма не ўсталявана, таму што яна несумяшчальная з вашым тэлефонам."
+ "Праграма не ўсталявана, таму што пакет, магчыма, з\'яўляецца несапраўдным."
+ "На гэтым планшэце немагчыма ўсталяваць прыкладанне %1$s."
+ "На вашым тэлевізары немагчыма ўсталяваць праграму %1$s."
+ "На гэтым тэлефоне немагчыма ўсталяваць прыкладанне %1$s."
+ "Адкрыць"
+ "Ваш адміністратар не дазваляе ўсталёўку праграм з невядомых крыніц."
+ "Гэты карыстальнік не можа ўсталёўваць невядомыя праграмы"
+ "Гэты карыстальнік не можа ўсталёўваць праграмы"
+ "ОК"
+ "Кіраванне прыкладаннямі"
+ "Не хапае месца"
+ "Немагчыма ўсталяваць прыкладанне %1$s. Вызваліце месца і паўтарыце спробу."
+ "Прыкладанне не знойдзена"
+ "Прыкладанне не знойдзена ў спісе ўсталяваных прыкладанняў."
+ "Забаронена"
+ "Бягучы карыстальнік не мае дазволу на гэта выдаленне."
+ "Памылка"
+ "Нельга выдаліць праграму."
+ "Выдалiць прыкладанне"
+ "Выдаліць абнаўленні"
+ "%1$s з\'яўляецца часткай наступнага прыкладання:"
+ "Выдаліць гэта прыкладанне?"
+ "Выдалiць гэта прыкладанне для ""ўсiх"" карыстальнirfў? Прыкладанне i яго дадзеныя будуць выдалены для ""ўсiх"" карыстальнiкаў прылады."
+ "Хочаце выдаліць гэту праграму для карыстальніка %1$s?"
+ "Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдалены."
+ "Замяніць гэту праграму заводскай версіяй? Усе даныя будуць выдаленыя. Гэта паўплывае на ўсіх карыстальнікаў гэтай прылады, у тым ліку карыстальнікаў з працоўнымі профілямі."
+ "Актыўныя выдаленні"
+ "Збоі выдалення"
+ "Выдаленне..."
+ "%1$s выдаляецца…"
+ "Выдаленне завершана"
+ "Выдалена %1$s"
+ "Няўдалае выдаленне."
+ "Не атрымалася выдаліць %1$s."
+ "Немагчыма выдаліць актыўную праграму адміністратара прылады"
+ "Немагчыма выдаліць актыўную праграму адміністратара прылады для карыстальніка %1$s"
+ "Гэта праграма патрабуецца для некаторых карыстальнікаў або профіляў і была выдалена для іншых"
+ "Гэта праграма неабходная для вашага профілю і не можа быць выдалена."
+ "Гэта праграма патрабуецца адміністратару вашай прылады і не можа быць выдалена."
+ "Праграмы адміністратара для кіравання прыладамі"
+ "Кіраванне карыстальнікамі"
+ "Немагчыма выдалiць прыкладанне %1$s."
+ "Памылка аналiзу пакета."
+ "Новыя"
+ "Усе"
+ "Прыватнасць"
+ "Доступ да прылады"
+ "Гэтае абнаўленне не патрабуе ніякіх новых дазволаў."
+ "Адмовіць"
+ "Дадатковая iнфармацыя"
+ "Усё роўна адмовіць"
+ "%1$s з %2$s"
+ "Дазволіць <b>%1$s</b> %2$s?"
+ "Заўсёды дазваляць праграме <b>%1$s</b> %2$s?"
+ "Толькі пры актыўнай праграме"
+ "Заўсёды"
+ "Адхіліць і больш не пытацца"
+ "Адключана: %1$d"
+ "усе адключаны"
+ "няма адключаных"
+ "Дазволіць"
+ "Праграмы"
+ "Дазволы праграм"
+ "Больш не пытацца"
+ "Няма дазволаў"
+ "Дадатковыя дазволы"
+ "Паказаць звесткі пра праграму"
+
+ %1$d іншы
+ %1$d іншыя
+ %1$d іншых
+ %1$d іншага
+
+ "Гэта праграма была распрацавана для больш старой версіі Android. Адхіленне дазволу можа прывесці да таго, што яна не будзе працаваць належным чынам."
+ "выканаць невядомае дзеянне"
+ "%1$d з %2$d праграм з дазволам"
+ "Паказаць сістэмныя"
+ "Схаваць сістэмныя"
+ "Няма праграм"
+ "Налады месцазнаходжання"
+ "%1$s з\'яўляецца службай вызначэння месцазнаходжання для гэтай прылады. Доступ да вызначэння месцазнаходжання можна змяніць у наладах вызначэння месцазнаходжання."
+ "Калі вы адхіліце гэты дазвол, асноўныя функцыі прылады могуць перастаць працаваць належным чынам."
+ "Ажыццёўлена палітыкай"
+ "Доступ у фонавым рэжыме адключаны згодна з правіламі"
+ "Доступ у фонавым рэжыме ўключаны згодна з правіламі"
+ "Доступ у актыўным рэжыме ўключаны згодна з правіламі"
+ "Кантралюецца адміністратарам"
+
+
+
+ "Заўсёды"
+ "Толькі пры актыўнай праграме"
+ "Ніколі"
+ "Загрузка..."
+ "Усе дазволы"
+ "Іншыя магчымасці праграмы"
+ "Запыт дазволу"
+ "Выяўлены слой экрана"
+ "Каб змяніць гэту наладу дазволу, вы павінны спачатку выключыць слой экрана з меню Налады > Праграмы"
+ "Адкрыць налады"
+ "Android Wear"
+ "Дзеянні па ўсталяванні або выдаленні не падтрымліваюцца на Wear."
+ "Выберыце, да чаго дазволіць доступ праграме <b>%1$s</b>"
+ "Праграма <b>%1$s</b> была абноўлена. Выберыце, да чаго ёй дазволіць доступ."
+ "Скасаваць"
+ "Далей"
+ "Новыя дазволы"
+ "Бягучыя дазволы"
+ "Падрыхтоўка праграмы..."
+ "Невядома"
+ "У мэтах бяспекі вашаму планшэту не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."
+ "У мэтах бяспекі вашаму тэлевізару не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."
+ "У мэтах бяспекі вашаму тэлефону не дазваляецца ўсталёўваць невядомыя праграмы з гэтай крыніцы."
+ "Ваш тэлефон і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні тэлефона ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."
+ "Ваш планшэт і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні планшэта ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."
+ "Ваш тэлевізар і асабістыя даныя больш уразлівыя для нападаў невядомых праграм. Пры ўсталёўцы гэтай праграмы вы згаджаецеся, што несяце адказнасць за любыя пашкоджанні тэлевізара ці страту даных, якія могуць адбыцца ў выніку выкарыстання гэтай праграмы."
+ "Працягнуць"
+ "Налады"
+ "Усталяванне/выдаленне праграм wear"
+
diff --git a/packages/PackageInstaller/res/values-bg-television/strings.xml b/packages/PackageInstaller/res/values-bg-television/strings.xml
new file mode 100644
index 0000000000000..7429955a3fb3d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Отказване, без повторно запитване"
+ "Можете да промените това по-късно от „Настройки“ > „Приложения“"
+ "%1$s/%2$s"
+ "Показване на системните приложения"
+ "Разрешения за приложението"
+ "Разрешения за приложението"
+ "Разрешения за %1$s"
+ "Допълнителни разрешения"
+ "Разрешения за %1$s"
+
diff --git a/packages/PackageInstaller/res/values-bg-watch/strings.xml b/packages/PackageInstaller/res/values-bg-watch/strings.xml
new file mode 100644
index 0000000000000..b71606b41f906
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Отказ, без повторно запитване"
+ "%1$s/%2$s"
+ "Показване на системните приложения"
+ "Без промяна"
+ "Да"
+ "Отказ"
+
diff --git a/packages/PackageInstaller/res/values-bg/strings.xml b/packages/PackageInstaller/res/values-bg/strings.xml
new file mode 100644
index 0000000000000..30d99eb8f9e79
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bg/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Инсталираща програма за пакети"
+ "Напред"
+ "Инсталиране"
+ "Готово"
+ "Назад"
+ "Инсталира се..."
+ "%1$s се инсталира…"
+ "Приложението бе инсталирано."
+ "Искате ли да инсталирате това приложение? То ще получи достъп до:"
+ "Искате ли да инсталирате това приложение? То не изисква никакъв специален достъп."
+ "Искате ли да инсталирате актуализация за това съществуващо приложение? Съществуващите ви данни няма да бъдат загубени. Актуализираното приложение ще получи достъп до:"
+ "Искате ли да инсталирате актуализация за това вградено приложение? Съществуващите ви данни няма да бъдат загубени. Актуализираното приложение ще получи достъп до:"
+ "Искате ли да инсталирате актуализация за това съществуващо приложение? Съществуващите ви данни няма да бъдат загубени. Не се изисква специален достъп."
+ "Искате ли да инсталирате актуализация за това вградено приложение? Съществуващите ви данни няма да бъдат загубени. Не се изисква специален достъп."
+ "Приложението не бе инсталирано."
+ "Инсталирането на пакета бе блокирано."
+ "Приложението не бе инсталирано, тъй като пакетът е в конфликт със съществуващ пакет."
+ "Приложението не бе инсталирано, тъй като не е съвместимо с таблета ви."
+ "Това приложение не е съвместимо с телевизора ви."
+ "Приложението не бе инсталирано, тъй като не е съвместимо с телефона ви."
+ "Приложението не бе инсталирано, тъй като изглежда, че пакетът е невалиден."
+ "%1$s не можа да се инсталира на таблета ви."
+ "%1$s не можа да се инсталира на телевизора ви."
+ "%1$s не можа да се инсталира на телефона ви."
+ "Отваряне"
+ "Администраторът ви не разрешава инсталирането на приложения, получени от неизвестни източници"
+ "Този потребител не може да инсталира неизвестни приложения"
+ "Този потребител няма разрешение да инсталира приложения"
+ "OK"
+ "Управление на приложенията"
+ "Няма място"
+ "%1$s не можа да се инсталира. Освободете място и опитайте отново."
+ "Приложението не бе намерено"
+ "Приложението не бе намерено в списъка с инсталирани приложения."
+ "Няма разрешение"
+ "Текущият потребител няма разрешение да извърши това деинсталиране."
+ "Грешка"
+ "Приложението не можа да бъде деинсталирано."
+ "Деинсталиране на приложението"
+ "Деинсталиране на актуализацията"
+ "%1$s е част от следното приложение:"
+ "Искате ли да деинсталирате това приложение?"
+ "Искате ли да деинсталирате това приложение за ""всички"" потребители? Приложението и данните му ще бъдат премахнати от ""всички"" потребители на устройството."
+ "Искате ли да деинсталирате това приложение за потребителя %1$s?"
+ "Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати."
+ "Това приложение да се замени ли с фабричната версия? Всички данни ще бъдат премахнати. Промяната ще засегне всеки потребител на устройството, включително тези със служебни потребителски профили."
+ "Активни деинсталирания"
+ "Неуспешни деинсталирания"
+ "Деинсталира се..."
+ "%1$s се деинсталира…"
+ "Деинсталирането завърши."
+ "Деинсталирахте %1$s"
+ "Деинсталирането не бе успешно."
+ "Деинсталирането на %1$s бе неуспешно."
+ "Активното приложение за администриране на устройството не може да се деинсталира"
+ "Активното приложение за администриране на устройството не може да се деинсталира за %1$s"
+ "Това приложение е необходимо за някои потребители или потребителски профили и бе деинсталирано за други."
+ "Това приложение е необходимо за потребителския ви профил и не може да се деинсталира."
+ "Приложението се изисква от администратора на у-вото и не може да се деинсталира."
+ "Управление на прилож. за администриране на у-вото"
+ "Управление на потребителите"
+ "%1$s не можа да се деинсталира."
+ "При синтактичния анализ на пакета възникна проблем."
+ "Нови"
+ "Всички"
+ "Поверителност"
+ "Достъп до у-вото"
+ "Тази актуализация не изисква нови разрешения."
+ "Отказване"
+ "Още информация"
+ "Отказване въпреки това"
+ "%1$s от %2$s"
+ "Разрешаване на <b>%1$s</b> да %2$s?"
+ "Винаги ли да се разрешава на <b>%1$s</b> да %2$s?"
+ "Само при използване на приложението"
+ "Винаги"
+ "Отказване, без повторно запитване"
+ "Деактивирахте %1$d"
+ "всички са деактивирани"
+ "няма деактивирани"
+ "Разрешаване"
+ "Приложения"
+ "Разрешения за приложения"
+ "Без повторно питане"
+ "Няма разрешения"
+ "Допълнителни разрешения"
+ "Отваряне на информацията за приложението"
+
+ Още %1$d
+ Още %1$d
+
+ "Това приложение е създадено за по-стара версия на Android. То може да спре да функционира нормално при отказване на разрешението."
+ "извършване на неизвестно действие"
+ "%1$d от %2$d приложения имат разрешение"
+ "Системни приложения"
+ "Скриване на системните"
+ "Няма приложения"
+ "Настройки за местоположението"
+ "%1$s е доставчик на услуги за местоположението за това устройство. Достъпът до местоположението може да бъде променен от съответните настройки."
+ "Ако откажете това разрешение, основни функции на устройството ви може да спрат да работят както трябва."
+ "Наложено чрез правило"
+ "Достъпът на заден план е деактивиран от правилата"
+ "Достъпът на заден план е активиран от правилата"
+ "Достъпът на преден план е активиран от правилата"
+ "Контролира се от администратор"
+
+
+
+ "Винаги"
+ "Само при използване на прилож."
+ "Никога"
+ "Зарежда се…"
+ "Всички разрешения"
+ "Други възможности на приложението"
+ "Заявка за разрешение"
+ "Открито е екранно наслагване"
+ "За да промените настройката за това разрешение, трябва първо да изключите екранното наслагване от „Настройки“ > „Приложения“"
+ "Отваряне на настройките"
+ "Android Wear"
+ "Действията инсталиране и деинсталиране не се поддържат на устройства с Wear."
+ "Изберете до какво да има достъп <b>%1$s</b>"
+ "Приложението <b>%1$s</b> е актуализирано. Изберете до какво да има достъп."
+ "Отказ"
+ "Напред"
+ "Нови разрешения"
+ "Текущи разрешения"
+ "Приложението се подготвя…"
+ "Неизвестно"
+ "От съображения за сигурност на таблета ви не могат да се инсталират неизвестни приложения от този източник."
+ "От съображения за сигурност на телевизора ви не могат да се инсталират неизвестни приложения от този източник."
+ "От съображения за сигурност на телефона ви не могат да се инсталират неизвестни приложения от този източник."
+ "Телефонът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на телефона или загуба на информация вследствие на използването на приложението."
+ "Таблетът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на таблета или загуба на информация вследствие на използването на приложението."
+ "Телевизорът и личните ви данни са по-уязвими към атаки от неизвестни приложения. С инсталирането на това приложение приемате, че носите отговорност при евентуална повреда на телевизора или загуба на информация вследствие на използването на приложението."
+ "Напред"
+ "Настройки"
+ "Инсталир./деинсталир. на прилож. за Wear"
+
diff --git a/packages/PackageInstaller/res/values-bn-television/strings.xml b/packages/PackageInstaller/res/values-bn-television/strings.xml
new file mode 100644
index 0000000000000..a83d7b81e9b02
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "অস্বীকার করুন এবং আবার জিজ্ঞাসা করবেন না"
+ "আপনি সেটিংস > অ্যাপ্লিকেশান এ এটি পরে পরিবর্তন করতে পারেন"
+ "%1$s / %2$s"
+ "সিস্টেম অ্যাপ্লিকেশানগুলি দেখান"
+ "অ্যাপ্লিকেশনের অনুমতি"
+ "অ্যাপ্লিকেশনের অনুমতি"
+ "%1$s অনুমতিগুলি"
+ "অতিরিক্ত অনুমতিগুলি"
+ "%1$s অনুমতিগুলি"
+
diff --git a/packages/PackageInstaller/res/values-bn-watch/strings.xml b/packages/PackageInstaller/res/values-bn-watch/strings.xml
new file mode 100644
index 0000000000000..79a91f484e7de
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "অস্বীকার করুন, আবার জিজ্ঞাসা করবেন না"
+ "%1$s / %2$s"
+ "সিস্টেম অ্যাপ্লিকেশানগুলি দেখান"
+ "পরিবর্তন করা যাবে না"
+ "হ্যাঁ"
+ "বাতিল করুন"
+
diff --git a/packages/PackageInstaller/res/values-bn/strings.xml b/packages/PackageInstaller/res/values-bn/strings.xml
new file mode 100644
index 0000000000000..c66f5bb2ebc1c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bn/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "প্যাকেজ ইনস্টলার"
+ "পরবর্তী"
+ "ইনস্টল করুন"
+ "সম্পন্ন হয়েছে"
+ "বাতিল করুন"
+ "ইনস্টল করা হচ্ছে…"
+ "%1$s ইন্সটল করা হচ্ছে…"
+ "অ্যাপ্লিকেশান ইনস্টল করা হয়েছে৷"
+ "আপনি কি এই অ্যাপ্লিকেশানটি ইনস্টল করতে চান? এর মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"
+ "আপনি কি এই অ্যাপ্লিকেশানটি ইনস্টল করতে চান? এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"
+ "আপনি কি এই বিদ্যমান অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এই আপডেট হওয়া অ্যাপ্লিকেশানটির মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"
+ "আপনি কি এই ভেতরে থাকা অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এই আপডেট হওয়া অ্যাপ্লিকেশানটির মাধ্যমে যেসব জিনিস অ্যাক্সেস করার সুবিধা পাবেন সেগুলি হল:"
+ "আপনি কি এই বিদ্যমান অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"
+ "আপনি কি ভেতরে থাকা অ্যাপ্লিকেশানের একটি আপডেট ইনস্টল করতে চান? আপনার বিদ্যমান ডেটাগুলি একই রকম থাকবে৷ এর জন্য কোনো বিশেষ অ্যাক্সেসের প্রয়োজন নেই৷"
+ "অ্যাপ্লিকেশান ইনস্টল করা হয়নি৷"
+ "ইনস্টল হওয়া থেকে প্যাকেজটিকে অবরুদ্ধ করা হয়েছে।"
+ "কোনো বিদ্যমান প্যাকেজের সাথে এই প্যাকেজটির বিবাদ থাকার ফলে অ্যাপ ইনস্টল করা হয়নি৷"
+ "অ্যাপটি আপনার ট্যাবলেটের জন্য উপযুক্ত না হওয়ার কারণে এটি ইনস্টল করা হয়নি৷"
+ "এই অ্যাপ্লিকেশানটি আপনার টিভির জন্য উপযুক্ত নয়৷"
+ "অ্যাপটি আপনার ফোনের জন্য উপযুক্ত না হওয়ার কারণে এটি ইনস্টল করা হয়নি৷"
+ "প্যাকেজটি অবৈধ বলে মনে হওয়ার কারণে অ্যাপ ইনস্টল করা হয়নি৷"
+ "%1$s আপনার ট্যাবলেটে ইনস্টল করা যায়নি৷"
+ "%1$s-কে আপনার টিভিতে ইনস্টল করা যাবে না৷"
+ "%1$s আপনার ফোনে ইনস্টল করা যায়নি৷"
+ "খুলুন"
+ "আপনার প্রশাসক অজানা উৎস থেকে প্রাপ্ত অ্যাপ ইনস্টল করার অনুমতি দেয় না"
+ "এই ব্যবহারকারী অজানা অ্যাপ ইনস্টল করতে পারবেন না"
+ "এই ব্যবহারকারী অ্যাপ ইনস্টল করার অনুমতি পাননি"
+ "ঠিক আছে"
+ "অ্যাপ্লিকেশানগুলির পরিচালনা করুন"
+ "পর্যাপ্ত জায়গা খালি নেই"
+ "%1$s ইনস্টল করা যায়নি৷ কিছু পরিমাণ জায়গা খালি করে আবার চেষ্টা করুন৷"
+ "অ্যাপ্লিকেশান পাওয়া যায়নি"
+ "অ্যাপ্লিকেশানটিকে ইনস্টল করা অ্যাপ্লিকেশানের তালিকাতে পাওয়া যায়নি৷"
+ "অনুমোদিত নয়"
+ "বর্তমান ব্যবহারকারী এই আনইনস্টলের কাজটি করার জন্য অনুমোদিত নয়৷"
+ "ত্রুটি"
+ "অ্যাপ আনইনস্টল করা গেল না৷"
+ "অ্যাপ্লিকেশানটিকে আনইনস্টল করুন"
+ "আপডেট আনইনস্টল করুন"
+ "%1$s হল নিম্নলিখিত অ্যাপ্লিকেশানগুলির অংশ বিশেষ:"
+ "আপনি কি এই অ্যাপ্লিকেশানটিকে আনইনস্টল করতে চান?"
+ "আপনি কি ""সমস্ত"" ব্যবহারকারীর জন্য এই অ্যাপ্লিকেশানটিকে আনইনস্টল করতে চান? এই ডিভাইসের ""সমস্ত"" ব্যবহারকারীর কাছ থেকে অ্যাপ্লিকেশানটি ও এর ডেটা হারিয়ে যাবে৷"
+ "আপনি কি ব্যবহারকারী %1$s এর জন্য এই অ্যাপ্লিকেশানটি আনইনস্টল করতে চান?"
+ "ফ্যাক্টরি সংস্করণের সাথে এই অ্যাপটিকে বদলাবেন? সব ডেটা মুছে যাবে।"
+ "ফ্যাক্টরি সংস্করণের সাথে এই অ্যাপটিকে বদলাবেন? সমস্ত ডেটা মুছে যাবে। এটি এই ডিভাইসের সমস্ত ব্যবহারকারী সহ তাদের কার্যের প্রোফাইলের উপরেও প্রভাব ফেলবে।"
+ "এগুলি আনইনস্টল করা হচ্ছে"
+ "এগুলি আনইনস্টল করা যায়নি"
+ "আনইনস্টল করা হচ্ছে ..."
+ "%1$s আনইনস্টল করা হচ্ছে…"
+ "আনইনস্টল সমাপ্ত হয়েছে৷"
+ "%1$s আনইনস্টল করা হয়েছে"
+ "আনইনস্টল সফল হয়নি৷"
+ "%1$s আনইনস্টল করা গেল না৷"
+ "সক্রিয় থাকা ডিভাইস প্রশাসক অ্যাপটি আনইনস্টল করা যাবে না"
+ "%1$s এর সক্রিয় থাকা ডিভাইস প্রশাসক অ্যাপটি আনইনস্টল করা যাবে না"
+ "কিছু ব্যবহারকারী বা প্রোফাইলের জন্য এই অ্যাপ্লিকেশানটি আবশ্যক এবং অন্যদের জন্য আনইনস্টল করা হবে"
+ "আপনার প্রোফাইলের জন্য এই অ্যাপ্লিকেশানটি প্রয়োজন এবং এটিকে আনইনস্টল করা যাবে না৷"
+ "আপনার ডিভাইস প্রশাসকের চাহিদা অনুযায়ী এই অ্যাপ্লিকেশানটি আবশ্যক এবং এটি আনইনস্টল করা যাবে না।"
+ "ডিভাইস প্রশাসক অ্যাপগুলি পরিচালনা করুন"
+ "ব্যবহারকারীদের পরিচালনা করুন"
+ "%1$s আনইনস্টল করা যায়নি৷"
+ "প্যাকেজটি বিশ্লেষণ করার ক্ষেত্রে একটি সমস্যা হয়েছে৷"
+ "নতুন"
+ "সমস্ত"
+ "গোপনীয়তা"
+ "ডিভাইসের অ্যাক্সেস"
+ "এই আপডেটের জন্য কোনো নতুন অনুমতির প্রয়োজন নেই৷"
+ "প্রত্যাখ্যান করুন"
+ "আরও তথ্য"
+ "যাইহোক অস্বীকার করুন"
+ "%2$s এর %1$s"
+ "<b>%1$s</b>কে %2$s এর অনুমতি দেবেন?"
+ "%2$s-এ সবসময় <b>%1$s</b> অনুমতি দেবেন?"
+ "অ্যাপটি ব্যবহার করার সময়"
+ "সবসময়"
+ "অস্বীকার করুন এবং আবার জিজ্ঞাসা করবেন না"
+ "%1$dটি অক্ষম করা হয়েছে"
+ "সমস্ত অক্ষম করা হয়েছে"
+ "কোনো কিছুই অক্ষম করা হয়নি"
+ "অনুমতি দিন"
+ "অ্যাপ"
+ "অ্যাপ্লিকেশনের অনুমতি"
+ "আর জিজ্ঞাসা করবেন না"
+ "কোনো অনুমতি নেই"
+ "অতিরিক্ত অনুমতিগুলি"
+ "অ্যাপের তথ্য দেখুন"
+
+ আরও %1$dটি
+ আরও %1$dটি
+
+ "একটি পুরোনো সংস্করণের Android এর জন্য এই অ্যাপ্লিকেশানটি ডিজাইন করা হয়েছিল৷ অনুমতি অস্বীকার করলে এটিকে যে কাজের উদ্দেশ্যে তৈরি করা হয়েছিল সেটি নাও করতে পারে৷"
+ "কোনো অজানা কার্য সঞ্চালন করুন"
+ "%2$dটির মধ্যে %1$dটি অ্যাপ্লিকেশান মঞ্জুরিপ্রাপ্ত"
+ "সিস্টেম দেখুন"
+ "সিস্টেম লুকান"
+ "কোনো অ্যাপ্লিকেশান নেই"
+ "লোকেশন সেটিংস"
+ "%1$s এই ডিভাইসের জন্য একটি লোকেশন পরিষেবাগুলি প্রদান করে। লোকেশন সেটিংস থেকে লোকেশনের অ্যাক্সেস পরিবর্তন করা যায়।"
+ "আপনি যদি এই অনুমতিটি অস্বীকার করেন, তবে আপনার ডিভাইসের প্রাথমিক বৈশিষ্ট্যগুলিকে যে কাজের উদ্দেশ্যে তৈরি করা হয়েছিল সেগুলি নাও করতে পারে৷"
+ "নীতি দ্বারা প্রয়োগ করা হয়েছে"
+ "নীতির কারণে ব্যাকগ্রাউন্ড অ্যাক্সেস বন্ধ করা আছে"
+ "নীতির কারণে ব্যাকগ্রাউন্ড অ্যাক্সেস চালু করা আছে"
+ "নীতির কারণে খুলে রাখা অ্যাপের অ্যাক্সেস চালু করা আছে"
+ "অ্যাডমিনের দ্বারা নিয়ন্ত্রিত"
+
+
+
+ "সবসময়"
+ "অ্যাপটি ব্যবহার করার সময়"
+ "কখনও না"
+ "লোড হচ্ছে..."
+ "সমস্ত অনুমতি"
+ "অন্যান্য অ্যাপ্লিকেশান ক্ষমতা"
+ "অনুমতির অনুরোধ"
+ "স্ক্রিন আচ্ছাদন শনাক্ত করা হয়েছে"
+ "এই অনুমতি সেটিংস পরিবর্তন করতে, আপনাকে প্রথমে সেটিংস > এ গিয়ে অ্যাপ্লিকেশানগুলি থেকে স্ক্রিন ওভারলে বন্ধ করতে হবে"
+ "সেটিংস খুলুন"
+ "Android Wear"
+ "ওয়েরে ইনস্টল/আনইনস্টল করার কাজগুলি সমর্থিত নয়।"
+ "<b>%1$s</b> কে কোন জিনিসগুলিতে অ্যাক্সেস দেবেন তা বেছে নিন"
+ "<b>%1$s</b> আপডেট করা হয়েছে৷ এই অ্যাপ্লিকেশানটিকে কোন জিনিসগুলিতে অ্যাক্সেস দেবেন তা চয়ন করুন৷"
+ "বাতিল করুন"
+ "চালিয়ে যান"
+ "নতুন অনুমতিগুলি"
+ "বর্তমান অনুমতিগুলি"
+ "অ্যাপ্লিকেশান স্টেজ করা হচ্ছে..."
+ "অজানা"
+ "আপনার নিরাপত্তার জন্য আপনার ট্যাবলেট কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"
+ "আপনার নিরাপত্তার জন্য আপনার TV কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"
+ "আপনার নিরাপত্তার জন্য আপনার ফোন কে এই উৎস থেকে আসা অজানা অ্যাপ ইনস্টল করার অনুমতি দেওয়া হয় না।"
+ "অজানা অ্যাপের দ্বারা আপনার ফোন এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার ফোনের বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"
+ "অজানা অ্যাপের দ্বারা আপনার ট্যাবলেট এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার ট্যাবলেটের বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"
+ "অজানা অ্যাপের দ্বারা আপনার টিভি এবং ব্যক্তিগত ডেটা আক্রান্ত হওয়ার সম্ভাবনা বেশি থাকে। এই অ্যাপটি ইনস্টল করার মাধ্যমে আপনি সম্মত হলেন যে এটি ব্যবহারের ফলে আপনার টিভি বা ডেটার কোনও ক্ষতি হলে তার জন্য আপনিই দায়ী থাকবেন।"
+ "চালিয়ে যান"
+ "সেটিংস"
+ "ওয়্যার অ্যাপ ইনস্টল/আনইনস্টল করা হচ্ছে"
+
diff --git a/packages/PackageInstaller/res/values-bs-television/strings.xml b/packages/PackageInstaller/res/values-bs-television/strings.xml
new file mode 100644
index 0000000000000..564f2a6c2aa36
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Odbij i ne pitaj ponovo"
+ "Ovo možete kasnije promijeniti u odjeljku Postavke > Aplikacije"
+ "%1$s/%2$s"
+ "Prikaži sistemske aplikacije"
+ "Odobrenja za aplikacije"
+ "Odobrenja za aplikacije"
+ "Odobrenja za: %1$s"
+ "Dodatna odobrenja"
+ "Odobrenja za: %1$s"
+
diff --git a/packages/PackageInstaller/res/values-bs-watch/strings.xml b/packages/PackageInstaller/res/values-bs-watch/strings.xml
new file mode 100644
index 0000000000000..dcae0979f8ce0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Odbij i ne pitaj ponovo"
+ "%1$s/%2$s"
+ "Prikaži sistemske aplikacije"
+ "Ne mijenja se"
+ "Da"
+ "Otkaži"
+
diff --git a/packages/PackageInstaller/res/values-bs/strings.xml b/packages/PackageInstaller/res/values-bs/strings.xml
new file mode 100644
index 0000000000000..3f2c5c35d16ca
--- /dev/null
+++ b/packages/PackageInstaller/res/values-bs/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Alat za instaliranje paketa"
+ "Naprijed"
+ "Instaliraj"
+ "Gotovo"
+ "Otkaži"
+ "Instalacija u toku..."
+ "Instaliranje %1$s…"
+ "Aplikacija je instalirana."
+ "Želite li instalirati ovu aplikaciju? Ona će dobiti pristup:"
+ "Želite li instalirati ovu aplikaciju? Ona ne zahtijeva poseban pristup."
+ "Želite li ažurirati ovu postojeću aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će dobiti pristup:"
+ "Želite li ažurirati ovu ugrađenu aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija će dobiti pristup:"
+ "Želite li ažurirati ovu postojeću aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Za ovo nije potreban poseban pristup."
+ "Želite li ažurirati ovu ugrađenu aplikaciju? Vaš postojeći podaci neće biti izgubljeni. Nije potreban poseban pristup."
+ "Aplikacija nije instalirana."
+ "Instaliranje ovog paketa je blokirano."
+ "Aplikacija nije instalirana jer paket nije usaglašen s postojećim paketom."
+ "Aplikacija nije instalirana jer nije kompatibilna s vašim tabletom."
+ "Ova aplikacija nije kompatibilna s vašim TV-om."
+ "Aplikacija nije instalirana jer nije kompatibilna s vašim telefonom."
+ "Aplikacija nije instalirana jer izgleda da paket nije važeći."
+ "Aplikaciju %1$s ne možete instalirati na svoj tablet."
+ "Nije moguće instalirati aplikaciju %1$s na vaš TV."
+ "Aplikaciju %1$s ne možete instalirati na svoj telefon."
+ "Otvori"
+ "Vaš administrator ne dozvoljava instaliranje aplikacija iz nepoznatih izvora."
+ "Ovaj korisnik ne može instalirati nepoznate aplikacije."
+ "Ovom korisniku nije dozvoljeno instaliranje aplikacija"
+ "Uredu"
+ "Upravljaj aplikacijama"
+ "Nedostatak prostora"
+ "Ne možete instalirati aplikaciju %1$s. Oslobodite prostora u pohrani i pokušajte ponovo."
+ "Aplikacija nije pronađena"
+ "Aplikacija nije pronađena na spisku instaliranih aplikacija."
+ "Nije dozvoljeno"
+ "Trenutnom korisniku nije dozvoljeno da izvrši ovu deinstalaciju."
+ "Greška"
+ "Nije bilo moguće deinstalirati aplikaciju."
+ "Uklanjanje aplikacije"
+ "Uklanjanje ažuriranja"
+ "%1$s je dio sljedeće aplikacije:"
+ "Želite li ukloniti ovu aplikaciju?"
+ "Želite li ukloniti ovu aplikaciju za "" sve "" korisnike? Aplikacija i njeni podaci će biti uklonjeni iz "" svih "" korisničkih računa na uređaju."
+ "Želite li ukloniti ovu aplikaciju za korisnika %1$s?"
+ "Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni."
+ "Želite li ovu aplikaciju zamijeniti s fabričkom verzijom? Svi podaci će biti uklonjeni. To će utjecati na sve korisnike uređaja, uključujući i one s radnim profilima."
+ "Tekuća deinstaliranja"
+ "Neuspjela deinstaliranja"
+ "Uklanjanje u toku..."
+ "Deinstalacija paketa %1$s…"
+ "Uklanjanje završeno."
+ "Deinstaliran je paket %1$s"
+ "Uklanjanje nije uspjelo."
+ "Paket %1$s nije uspješno deinstaliran."
+ "Nije moguće deinstalirati aktivnu aplikaciju administratora uređaja"
+ "Nije moguće deinstalirati aktivnu aplikaciju administratora uređaja za korisnika %1$s"
+ "Ova aplikacija je neophodna nekim korisnicima ili profilima, a kod ostalih je deinstalirana"
+ "Ova aplikacija je potrebna za vaš profil i ne može se deinstalirati."
+ "Ova aplikacija je potrebna administratoru vašeg uređaja i ne može se ukloniti."
+ "Upravljajte aplikacijama administratora uređaja"
+ "Upravljanje korisnicima"
+ "Aplikacija %1$s se ne može ukloniti."
+ "Došlo je do problema prilikom raščlanjivanja paketa."
+ "Novo"
+ "Sve"
+ "Privatnost"
+ "Pristup uređaju"
+ "Za ovo ažuriranje nisu potrebne nova odobrenja."
+ "Odbij"
+ "Više informacija"
+ "Odbij svakako"
+ "%1$s od %2$s"
+ "Dozvoliti aplikaciji <b>%1$s</b> da %2$s?"
+ "Uvijek dozvoliti da aplikacija <b>%1$s</b> %2$s?"
+ "Samo dok se koristi aplikacija"
+ "Uvijek"
+ "Odbij i ne pitaj ponovo"
+ "omogućeno je %1$d"
+ "sve je onemogućeno"
+ "ništa nije onemogućeno"
+ "Dozvoli"
+ "Aplikacije"
+ "Odobrenja za aplikacije"
+ "Ne pitaj ponovo"
+ "Nema odobrenja"
+ "Dodatna odobrenja"
+ "Otvori informacije o aplikaciji"
+
+ još %1$d
+ još %1$d
+ još %1$d
+
+ "Ova aplikacija je kreirana za stariju verziju Androida. Odbijanje odobrenja može uzrokovati da ona više ne funkcionira onako kako je primarno zamišljeno."
+ "izvrši nepoznatu radnju"
+ "Aplikacije sa odobrenjem: %1$d od %2$d"
+ "Prikaži sistemske aplikacije"
+ "Sakrij sistemske"
+ "Nijedna aplikacija"
+ "Postavke lokacije"
+ "%1$s pruža usluge lokacije za ovaj uređaj. Pristup lokaciji se može izmijeniti u postavkama lokacije."
+ "Ukoliko odbijete ovo odobrenje, osnovne funkcije vašeg uređaja možda više neće funkcionirati onako kako je prvobitno zamišljeno."
+ "Nametnuto je pravilima"
+ "Pristup pozadini je onemogućen u skladu s pravilima"
+ "Pristup pozadini je omogućen u skladu s pravilima"
+ "Pristup u prvom planu je omogućen u skladu s pravilima"
+ "Kontrolira administrator"
+
+
+
+ "Uvijek"
+ "Samo dok se koristi aplikacija"
+ "Nikada"
+ "Učitava se…"
+ "Sva odobrenja"
+ "Ostale mogućnosti aplikacije"
+ "Zahtjev za odobrenjem"
+ "Otkriven je element koji prekriva sadržaj ekrana"
+ "Da promijenite postavku ovog odobrenja, prvo morate isključiti element koji prekriva sadržaj ekrana u odjeljku Postavke > Aplikacije"
+ "Otvori postavke"
+ "Android Wear"
+ "Instaliranje/deinstaliranje nije podržano na Wearu."
+ "Odaberite čemu aplikacija <b>%1$s≪/b> može pristupiti"
+ "Aplikacija ≪b>%1$s≪/b> je ažurirana. Odaberite čemu ova aplikacija može pristupiti."
+ "Otkaži"
+ "Nastavi"
+ "Nova odobrenja"
+ "Postojeća odobrenja"
+ "Aplikacija se postavlja…"
+ "Nepoznato"
+ "Vašem tabletu iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Vašem TV-u iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Vašem telefonu iz sigurnosnih razloga nije dopušteno instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Vaši podaci na telefonu i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na telefonu ili gubitak podataka do kojih može doći korištenjem aplikacije."
+ "Vaši podaci na tabletu i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na tabletu ili gubitak podataka do kojih može doći korištenjem aplikacije."
+ "Vaši podaci na TV-u i vaši lični podaci izloženiji su napadima nepoznatih aplikacija. Instaliranjem ove aplikacije, saglasni ste da ste vi odgovorni za bilo kakvu štetu na TV-u ili gubitak podataka do kojih može doći korištenjem aplikacije."
+ "Nastavi"
+ "Postavke"
+ "(De)instaliranje wear aplikacija"
+
diff --git a/packages/PackageInstaller/res/values-ca-television/strings.xml b/packages/PackageInstaller/res/values-ca-television/strings.xml
new file mode 100644
index 0000000000000..7126c41c552a8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Denega i no m\'ho tornis a preguntar"
+ "Pots canviar-ho més endavant a Configuració > Aplicacions"
+ "%1$s/%2$s"
+ "Mostra les aplicacions del sistema"
+ "Permisos d\'aplicacions"
+ "Permisos d\'aplicacions"
+ "Permisos: %1$s"
+ "Permisos addicionals"
+ "Permisos: %1$s"
+
diff --git a/packages/PackageInstaller/res/values-ca-watch/strings.xml b/packages/PackageInstaller/res/values-ca-watch/strings.xml
new file mode 100644
index 0000000000000..d289b5606b235
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Denega i no m\'ho demanis més"
+ "%1$s/%2$s"
+ "Mostra les aplicacions del sistema"
+ "No es pot canviar"
+ "Sí"
+ "Cancel·la"
+
diff --git a/packages/PackageInstaller/res/values-ca/strings.xml b/packages/PackageInstaller/res/values-ca/strings.xml
new file mode 100644
index 0000000000000..62d758c652118
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ca/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instal·lador de paquets"
+ "Següent"
+ "Instal·la"
+ "Fet"
+ "Cancel·la"
+ "S\'està instal·lant..."
+ "S\'està instal·lant %1$s…"
+ "Aplicació instal·lada."
+ "Vols instal·lar aquesta aplicació? Tindrà els permisos següents:"
+ "Vols instal·lar aquesta aplicació? No requereix cap accés especial."
+ "Vols instal·lar una actualització per a aquesta aplicació? No es perdran les teves dades existents. L\'aplicació actualitzada tindrà els permisos següents:"
+ "Vols instal·lar una actualització d\'aquesta aplicació integrada? No es perdran les teves dades. L\'aplicació actualitzada tindrà els permisos següents:"
+ "Vols instal·lar una actualització a aquesta aplicació existent? Les dades existents no es perdran. No cal cap tipus d\'accés especial."
+ "Vols instal·lar una actualització a aquesta aplicació integrada? Les teves dades existents no es perdran. No cal cap tipus d\'accés especial."
+ "L\'aplicació no s\'ha instal·lat."
+ "El paquet s\'ha bloquejat perquè no es pugui instal·lar."
+ "L\'aplicació no s\'ha instal·lat perquè el paquet entra en conflicte amb un d\'existent."
+ "L\'aplicació no s\'ha instal·lat perquè no és compatible amb la teva tauleta."
+ "Aquesta aplicació no és compatible amb el teu televisor."
+ "L\'aplicació no s\'ha instal·lat perquè no és compatible amb el teu telèfon."
+ "L\'aplicació no s\'ha instal·lat perquè sembla que el paquet no és vàlid."
+ "%1$s no s\'ha pogut instal·lar a la tauleta."
+ "%1$s no s\'ha pogut instal·lar al televisor."
+ "%1$s no s\'ha pogut instal·lar al telèfon."
+ "Obre"
+ "L\'administrador no permet instal·lar aplicacions de fonts desconegudes"
+ "Aquest usuari no pot instal·lar aplicacions desconegudes"
+ "Aquest usuari no té permís per instal·lar aplicacions"
+ "D\'acord"
+ "Gestiona les aplicacions"
+ "Espai esgotat"
+ "No s\'ha pogut instal·lar %1$s. Allibera espai i torna-ho a provar."
+ "No s\'ha trobat l\'aplicació"
+ "No s\'ha trobat l\'aplicació a la llista d\'aplicacions instal·lades."
+ "Sense autorització"
+ "L\'usuari actual no té permís per dur a terme aquesta desinstal·lació."
+ "Error"
+ "No s\'ha pogut desinstal·lar l\'aplicació."
+ "Desinstal·la l\'aplicació"
+ "Desinstal·la l\'actualització"
+ "%1$s forma part de l\'aplicació següent:"
+ "Vols desinstal·lar aquesta aplicació?"
+ "Vols desinstal·lar aquesta aplicació per a ""tots"" els usuaris? L\'aplicació i les seves dades se suprimiran per a ""tots"" els usuaris del dispositiu."
+ "Vols desinstal·lar aquesta aplicació per a l\'usuari %1$s?"
+ "Si substitueixes aquesta aplicació per la versió de fàbrica, s\'esborraran totes les dades."
+ "Si substitueixes aquesta aplicació per la versió de fàbrica, s\'esborraran totes les dades. Això afectarà tots els usuaris d\'aquest dispositiu, inclosos els que tinguin un perfil professional."
+ "Desinstal·lacions en curs"
+ "Desinstal·lacions fallides"
+ "S\'està desinstal·lant..."
+ "S\'està desinstal·lant %1$s…"
+ "Desinstal·lació finalitzada."
+ "S\'ha desinstal·lat %1$s"
+ "S\'ha produït un error en la desinstal·lació."
+ "No s\'ha pogut desinstal·lar %1$s correctament."
+ "No es pot desinstal·lar l\'aplicació activa de l\'administrador del dispositiu"
+ "No es pot desinstal·lar l\'aplicació activa de l\'administrador del dispositiu per a %1$s"
+ "L\'aplicació cal en alguns usuaris o perfils i s\'ha desinstal·lat per a d\'altres"
+ "Aquesta aplicació es necessita per al teu perfil i no es pot desinstal·lar."
+ "L\'administrador del dispositiu necessita l\'aplicació i no la pots desinstal·lar."
+ "Gestiona aplicacions d\'administració del dispositiu"
+ "Gestiona els usuaris"
+ "%1$s no s\'ha pogut desinstal·lar."
+ "S\'ha produït un problema en analitzar el paquet."
+ "Nous"
+ "Tots"
+ "Privadesa"
+ "Accés al dispositiu"
+ "Aquesta actualització no requereix permisos nous."
+ "Denega"
+ "Més informació"
+ "Denega de totes maneres"
+ "%1$s de %2$s"
+ "Vols permetre a <b>%1$s</b> %2$s?"
+ "Vols permetre que <b>%1$s</b> %2$s?"
+ "Només mentre s\'utilitzi l\'aplicació"
+ "Sempre"
+ "Denega i no m\'ho tornis a preguntar"
+ "%1$d permisos desactivats"
+ "tots els permisos desactivats"
+ "cap permís desactivat"
+ "Permet"
+ "Aplicacions"
+ "Permisos d\'aplicacions"
+ "No m\'ho tornis a preguntar"
+ "Sense permisos"
+ "Més permisos"
+ "Obre la informació de l\'aplicació"
+
+ %1$d més
+ %1$d més
+
+ "Aquesta aplicació es va dissenyar per a una versió anterior d\'Android. És possible que no funcioni com està previst si li denegues el permís."
+ "dur a terme una acció desconeguda"
+ "%1$d de %2$d aplicacions permeses"
+ "Mostra aplicacions del sistema"
+ "Amaga aplicacions del sistema"
+ "Cap aplicació"
+ "Configuració d\'ubicació"
+ "%1$s és un proveïdor de serveis d\'ubicació per a aquest dispositiu. L\'accés a la ubicació es pot modificar des de la configuració d\'ubicació."
+ "Si rebutges aquest permís, és possible que funcions bàsiques del dispositiu deixin de funcionar correctament."
+ "Aplicat en funció de la política"
+ "S\'ha desactivat l\'accés en segon pla per la política"
+ "S\'ha activat l\'accés en segon pla per la política"
+ "S\'ha activat l\'accés en primer pla per la política"
+ "Controlat per l\'administrador"
+
+
+
+ "Sempre"
+ "Només mentre s\'utilitzi l\'app"
+ "Mai"
+ "S\'està carregant..."
+ "Tots els permisos"
+ "Altres competències de l\'aplicació"
+ "Sol·licitud de permís"
+ "S\'ha detectat una superposició de pantalla"
+ "Per canviar la configuració de permisos, cal que desactivis la superposició de pantalla des de Configuració > Aplicacions"
+ "Obre Configuració"
+ "Android Wear"
+ "Les accions d\'instal·lar o de desinstal·lar no s\'admeten a Wear."
+ "Tria a què vols que tingui accés <b>%1$s</b>"
+ "S\'ha actualitzat <b>%1$s</b>. Tria a què vols que tingui accés aquesta aplicació."
+ "Cancel·la"
+ "Continua"
+ "Permisos nous"
+ "Permisos actuals"
+ "S\'està preparant la instal·lació de l\'aplicació…"
+ "Desconegut"
+ "Per seguretat, la tauleta no pot instal·lar aplicacions desconegudes d\'aquesta font."
+ "Per seguretat, el televisor no pot instal·lar aplicacions desconegudes d\'aquesta font."
+ "Per seguretat, el telèfon no pot instal·lar aplicacions desconegudes d\'aquesta font."
+ "El telèfon i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al telèfon o de la pèrdua de dades que pugui resultar del seu ús."
+ "La tauleta i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi a la tauleta o de la pèrdua de dades que pugui resultar del seu ús."
+ "El televisor i les dades personals són més vulnerables als atacs d\'aplicacions desconegudes. En instal·lar aquesta aplicació, acceptes que ets responsable de qualsevol dany que es produeixi al televisor o de la pèrdua de dades que pugui resultar del seu ús."
+ "Continua"
+ "Configuració"
+ "Instal·lant o desinstal·lant aplicacions de Wear"
+
diff --git a/packages/PackageInstaller/res/values-cs-television/strings.xml b/packages/PackageInstaller/res/values-cs-television/strings.xml
new file mode 100644
index 0000000000000..ed2d8dc5a5376
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Odmítnout a již se neptat"
+ "Svoji volbu můžete později změnit v nabídce Nastavení > Aplikace."
+ "%1$s / %2$s"
+ "Zobrazit systémové aplikace"
+ "Oprávnění aplikací"
+ "Oprávnění aplikací"
+ "%1$s – oprávnění"
+ "Další oprávnění"
+ "%1$s – oprávnění"
+
diff --git a/packages/PackageInstaller/res/values-cs-watch/strings.xml b/packages/PackageInstaller/res/values-cs-watch/strings.xml
new file mode 100644
index 0000000000000..160d7aa189ff6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Odmítnout a již se neptat"
+ "%1$s/%2$s"
+ "Zobrazit systémové aplikace"
+ "Nelze změnit"
+ "Ano"
+ "Zrušit"
+
diff --git a/packages/PackageInstaller/res/values-cs/strings.xml b/packages/PackageInstaller/res/values-cs/strings.xml
new file mode 100644
index 0000000000000..a64c07a5f3b2e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-cs/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Nástroj k instalaci balíčků"
+ "Další"
+ "Instalovat"
+ "Hotovo"
+ "Zrušit"
+ "Probíhá instalace..."
+ "Instalace balíčku %1$s…"
+ "Aplikace je nainstalována."
+ "Chcete tuto aplikaci nainstalovat? Aplikace získá přístup k těmto oprávněním:"
+ "Chcete tuto aplikaci nainstalovat? Aplikace nevyžaduje žádná zvláštní oprávnění."
+ "Chcete nainstalovat aktualizaci této existující aplikace? Stávající data nebudou ztracena. Aktualizovaná aplikace získá přístup k následujícímu:"
+ "Chcete nainstalovat aktualizaci této integrované aplikace? Stávající data nebudou ztracena. Aktualizovaná aplikace získá přístup k následujícímu:"
+ "Chcete nainstalovat aktualizaci této existující aplikace? Vaše existující data nebudou ztracena. Není vyžadován žádný zvláštní přístup."
+ "Chcete nainstalovat aktualizaci této integrované aplikace? Vaše existující data nebudou ztracena. Není vyžadován žádný zvláštní přístup."
+ "Aplikaci nelze nainstalovat."
+ "Instalace balíčku byla zablokována."
+ "Aplikaci nelze nainstalovat, protože balíček je v konfliktu se stávajícím balíčkem."
+ "Aplikaci nelze nainstalovat, protože s tabletem není kompatibilní."
+ "Tato aplikace s vaší televizí není kompatibilní."
+ "Aplikaci nelze nainstalovat, protože s telefonem není kompatibilní."
+ "Aplikaci nelze nainstalovat, protože balíček zřejmě není platný."
+ "Aplikaci %1$s do tohoto tabletu nelze nainstalovat."
+ "Aplikaci %1$s se do televize nepodařilo nainstalovat."
+ "Aplikaci %1$s do tohoto telefonu nelze nainstalovat."
+ "Otevřít"
+ "Váš administrátor nedovoluje instalaci aplikací z neznámých zdrojů"
+ "Tento uživatel nemůže instalovat neznámé aplikace"
+ "Tento uživatel nesmí instalovat aplikace"
+ "OK"
+ "Spravovat aplikace"
+ "Nedostatek místa"
+ "Aplikaci %1$s nelze nainstalovat. Uvolněte místo v paměti a zkuste to znovu."
+ "Aplikace nebyla nalezena"
+ "Aplikaci se nepodařilo najít na seznamu nainstalovaných aplikací."
+ "Není povoleno"
+ "Aktuální uživatel nemá k odinstalaci oprávnění."
+ "Chyba"
+ "Aplikaci nelze odinstalovat."
+ "Odinstalovat aplikaci"
+ "Odinstalovat aktualizaci"
+ "Činnost %1$s je součástí následující aplikace:"
+ "Chcete tuto aplikaci odinstalovat?"
+ "Chcete tuto aplikaci odinstalovat ""všem"" uživatelům? Aplikace a její údaje budou odstraněny ""všem"" uživatelům tohoto zařízení."
+ "Chcete tuto aplikaci pro uživatele %1$s odinstalovat?"
+ "Chcete tuto aplikaci nahradit tovární verzí? Všechna data budou odstraněna."
+ "Chcete tuto aplikaci nahradit tovární verzí? Všechna data budou odstraněna. Tato akce ovlivní všechny uživatele zařízení, včetně uživatelů s pracovním profilem."
+ "Probíhající odinstalace"
+ "Neúspěšné odinstalace"
+ "Probíhá odinstalace..."
+ "Odinstalace balíčku %1$s…"
+ "Odinstalace byla dokončena."
+ "Balíček %1$s byl odinstalován"
+ "Odinstalace se nezdařila."
+ "Odinstalace balíčku %1$s se nezdařila."
+ "Aktivní aplikaci pro správu zařízení nelze odinstalovat"
+ "Aktivní aplikaci pro správu zařízení uživatele %1$s nelze odinstalovat"
+ "Tato aplikace je u některých uživatelů nebo profilů požadována, u ostatních byla odinstalována."
+ "Tato aplikace je pro váš profil požadována a nelze ji odinstalovat."
+ "Tato aplikace je administrátorem zařízení vyžadována a nelze ji odinstalovat."
+ "Přejít do nastavení aplikací pro správu zařízení"
+ "Správa uživatelů"
+ "Aplikaci %1$s nelze odinstalovat."
+ "Při analýze balíčku došlo k chybě."
+ "Nově"
+ "Vše"
+ "Ochrana soukromí"
+ "Přístup k zařízení"
+ "Tato aktualizace nevyžaduje žádná nová oprávnění."
+ "Odmítnout"
+ "Další informace"
+ "Zamítnout"
+ "%1$s z %2$s"
+ "Povolit aplikaci <b>%1$s</b> %2$s?"
+ "Vždy povolit aplikaci <b>%1$s</b> %2$s?"
+ "Pouze při používání aplikace"
+ "Vždy"
+ "Odmítnout a už se neptat"
+ "zakázáno (%1$d)"
+ "vše zakázáno"
+ "nic nezakázáno"
+ "Povolit"
+ "Aplikace"
+ "Oprávnění aplikací"
+ "Příště se neptat"
+ "Žádná oprávnění"
+ "Další oprávnění"
+ "Otevřít informace o aplikaci"
+
+ Ještě %1$d
+ Ještě %1$d
+ Ještě %1$d
+ Ještě %1$d
+
+ "Tato aplikace byla vytvořena pro starší verzi platformy Android. Pokud oprávnění neudělíte, může přestat fungovat podle původního záměru."
+ "provést neznámou akci"
+ "Povoleno u %1$d z %2$d aplikací"
+ "Zobrazit systémové aplikace"
+ "Skrýt systémové aplikace"
+ "Žádné aplikace"
+ "Nastavení polohy"
+ "Služby určování polohy v tomto zařízení poskytuje aplikace %1$s. Přístup k poloze lze upravit v nastavení polohy."
+ "Pokud toto oprávnění zamítnete, základní funkce zařízení nemusejí fungovat správně."
+ "Vynuceno zásadami"
+ "Přístup na pozadí byl zakázán zásadami"
+ "Přístup na pozadí byl povolen zásadami"
+ "Přístup na popředí byl povolen zásadami"
+ "Spravováno administrátorem"
+
+
+
+ "Vždy"
+ "Pouze při používání aplikace"
+ "Nikdy"
+ "Načítání…"
+ "Všechna oprávnění"
+ "Ostatní oprávnění aplikace"
+ "Žádost o oprávnění"
+ "Byla zjištěna překryvná vrstva obrazovky"
+ "Chcete-li změnit nastavení tohoto oprávnění, v Nastavení > Aplikace je třeba nejprve vypnout překryvnou vrstvu obrazovky"
+ "Otevřít nastavení"
+ "Android Wear"
+ "Akce instalace/odinstalace nejsou v zařízení Wear podporovány."
+ "Určete, k čemu aplikaci <b>%1$s</b> povolíte přístup"
+ "Aplikace <b>%1$s</b> byla aktualizována. Určete, k čemu jí povolíte přístup."
+ "Zrušit"
+ "Pokračovat"
+ "Nová oprávnění"
+ "Aktuální oprávnění"
+ "Příprava instalace…"
+ "Neznámá aplikace"
+ "Z bezpečnostních důvodů do tabletu není dovoleno instalovat neznámé aplikace z tohoto zdroje."
+ "Z bezpečnostních důvodů do televize není dovoleno instalovat neznámé aplikace z tohoto zdroje."
+ "Z bezpečnostních důvodů do telefonu není dovoleno instalovat neznámé aplikace z tohoto zdroje."
+ "Telefon a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na telefonu nebo ztrátu dat, která může být používáním aplikace způsobena."
+ "Tablet a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na tabletu nebo ztrátu dat, která může být používáním aplikace způsobena."
+ "Televize a osobní údaje jsou zranitelnější vůči útoku ze strany neznámých aplikací. Instalací této aplikace přijímáte odpovědnost za případné škody na televizi nebo ztrátu dat, která může být používáním aplikace způsobena."
+ "Pokračovat"
+ "Nastavení"
+ "Instalace/odinstalace aplikací pro Wear"
+
diff --git a/packages/PackageInstaller/res/values-da-television/strings.xml b/packages/PackageInstaller/res/values-da-television/strings.xml
new file mode 100644
index 0000000000000..f9c0da296c3b9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Afvis, og spørg ikke igen"
+ "Du kan altid ændre dette i Indstillinger > Apps"
+ "%1$s/%2$s"
+ "Vis systemapps"
+ "Apptilladelser"
+ "Apptilladelser"
+ "%1$s-tilladelser"
+ "Flere tilladelser"
+ "%1$s-tilladelser"
+
diff --git a/packages/PackageInstaller/res/values-da-watch/strings.xml b/packages/PackageInstaller/res/values-da-watch/strings.xml
new file mode 100644
index 0000000000000..616b1c2e4669d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Afvis, og spørg ikke igen"
+ "%1$s/%2$s"
+ "Vis systemapps"
+ "Kan ikke ændres"
+ "Ja"
+ "Annuller"
+
diff --git a/packages/PackageInstaller/res/values-da/strings.xml b/packages/PackageInstaller/res/values-da/strings.xml
new file mode 100644
index 0000000000000..73d03a5c3a03d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-da/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pakkeinstallationsprogram"
+ "Næste"
+ "Installer"
+ "Afslut"
+ "Annuller"
+ "Installerer..."
+ "Installerer %1$s…"
+ "Appen er installeret."
+ "Vil du installere denne applikation? Den får adgang til følgende:"
+ "Vil du installere denne applikation? Den kræver ingen særlig adgang."
+ "Vil du installere en opdatering til den eksisterende app? Du mister ikke dine eksisterende data. Den opdaterede app kan gøre følgende:"
+ "Vil du installere en opdatering til den indbyggede app? Du mister ikke dine eksisterende data. Den opdaterede app kan gøre følgende:"
+ "Vil du installere en opdatering til denne eksisterende applikation? Dine eksisterende data vil ikke gå tabt. Det kræver ikke nogen særlig adgang."
+ "Vil du installere en opdatering til denne indbyggede applikation? Dine eksisterende data vil ikke gå tabt. Det kræver ikke nogen særlig adgang."
+ "Appen blev ikke installeret."
+ "Pakken blev blokeret i at blive installeret."
+ "Appen blev ikke installeret, da pakken er i strid med en eksisterende pakke."
+ "Appen blev ikke installeret, da den er ikke kompatibel med din tablet."
+ "Denne app er ikke kompatibel med dit fjernsyn."
+ "Appen blev ikke installeret, da den ikke er kompatibel med din telefon."
+ "Appen blev ikke installeret, da pakken ser ud til at være ugyldig."
+ "%1$s kunne ikke installeres på din tablet."
+ "%1$s kunne ikke installeres på dit tv."
+ "%1$s kunne ikke installeres på din telefon."
+ "Åbn"
+ "Din administrator tillader ikke installation af apps, der hentes fra ukendte kilder"
+ "Denne bruger kan ikke installere ukendte apps"
+ "Denne bruger har ikke tilladelse til at installere apps"
+ "OK"
+ "Administrer apps"
+ "Der er ikke mere plads"
+ "%1$s kunne ikke installeres. Frigør noget plads, og prøv igen."
+ "Appen blev ikke fundet"
+ "Appen blev ikke fundet på listen over installerede apps."
+ "Ikke tilladt"
+ "Den nuværende bruger har ikke tilladelse til at udføre denne afinstallation."
+ "Fejl"
+ "Appen kunne ikke afinstalleres."
+ "Afinstaller appen"
+ "Afinstaller opdatering"
+ "%1$s er en del af følgende app:"
+ "Vil du afinstallere denne app?"
+ "Vil du afinstallere denne app for ""alle"" brugere? Applikationen og dens data vil blive fjernet fra ""alle"" brugere på denne enhed."
+ "Vil du afinstallere denne app for brugeren %1$s?"
+ "Vil du erstatte denne app med fabriksversionen? Alle data fjernes."
+ "Vil du erstatte denne app med fabriksversionen? Alle data fjernes. Dette påvirker alle brugere af denne enhed, herunder de brugere, der har arbejdsprofiler."
+ "Igangværende afinstallationer"
+ "Mislykkede afinstallationer"
+ "Afinstallerer..."
+ "Afinstallerer %1$s…"
+ "Afinstallationen er afsluttet."
+ "%1$s blev afinstalleret"
+ "Afinstallationen mislykkedes."
+ "%1$s kunne ikke afinstalleres."
+ "Den aktive app til enhedsadministration kan ikke afinstalleres"
+ "Den aktive app til enhedsadministration for %1$s kan ikke afinstalleres"
+ "Denne app kræves for nogle brugere eller profiler og afinstalleres for andre"
+ "Denne app er nødvendig for din profil og kan ikke afinstalleres."
+ "Denne app er påkrævet af din enhedsadministrator og kan ikke afinstalleres."
+ "Administrer apps til enhedsadministration"
+ "Administrer brugere"
+ "%1$s kunne ikke afinstalleres."
+ "Der opstod et problem med parsing af pakken."
+ "Ny"
+ "Alle"
+ "Privatliv"
+ "Adgang til enheden"
+ "Denne opdatering kræver ingen nye tilladelser."
+ "Afvis"
+ "Få flere oplysninger"
+ "Afvis alligevel"
+ "%1$s ud af %2$s"
+ "Vil du give <b>%1$s</b> tilladelse til at %2$s?"
+ "Skal <b>%1$s</b> altid have tilladelse til at %2$s?"
+ "Kun mens appen bruges"
+ "Altid"
+ "Afvis, og spørg ikke igen"
+ "%1$d er deaktiveret"
+ "alle er deaktiveret"
+ "ingen er deaktiveret"
+ "Tillad"
+ "Apps"
+ "Apptilladelser"
+ "Spørg ikke igen"
+ "Ingen tilladelser"
+ "Flere tilladelser"
+ "Åbn appinfo"
+
+ %1$d mere
+ %1$d mere
+
+ "Denne app er udviklet til en ældre version af Android. Hvis du ikke giver den tilladelse, vil den muligvis ikke længere virke efter hensigten."
+ "udføre en ukendt handling"
+ "%1$d ud af %2$d apps har tilladelse"
+ "Vis systemapps"
+ "Skjul systemapps"
+ "Ingen apps"
+ "Placeringsindstillinger"
+ "%1$s udbyder placeringstjenester for denne enhed. Adgangen til din placering kan ændres i Placeringsindstillinger."
+ "Hvis du afviser denne tilladelse, vil grundlæggende funktioner på din enhed muligvis ikke længere fungere efter hensigten."
+ "Håndhæves af politik"
+ "Adgang i baggrunden er deaktiveret af en politik"
+ "Adgang i baggrunden er aktiveret af en politik"
+ "Adgang i forgrunden er aktiveret af en politik"
+ "Styres af administratoren"
+
+
+
+ "Altid"
+ "Kun mens appen bruges"
+ "Aldrig"
+ "Indlæser…"
+ "Alle tilladelser"
+ "Andre app-egenskaber"
+ "Anmodning om tilladelse"
+ "Der er registreret skærmoverlejring"
+ "Hvis du vil ændre denne indstilling for tilladelser, skal du først slå skærmoverlejringen fra i Indstillinger > Apps"
+ "Åbn indstillingerne"
+ "Android Wear"
+ "Det er ikke muligt at installere/afinstallere på Wear."
+ "Vælg, hvad <b>%1$s</b> må få adgang til"
+ "<b>%1$s</b> er blevet opdateret. Vælg, hvad denne app må få adgang til."
+ "Annuller"
+ "Fortsæt"
+ "Nye tilladelser"
+ "Aktuelle tilladelser"
+ "Forbereder appen…"
+ "Ukendt"
+ "Din tablet har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."
+ "Dit fjernsyn har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."
+ "Din telefon har af sikkerhedshensyn ikke tilladelse til at installere ukendte apps fra denne kilde."
+ "Din telefon og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din telefon eller tab af data, der kan skyldes brug af appen."
+ "Din tablet og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på din tablet eller tab af data, der kan skyldes brug af appen."
+ "Dit fjernsyn og dine personlige data er mere sårbare over for angreb fra ukendte apps. Når du installerer denne app, accepterer du, at du er ansvarlig for skader på dit fjernsyn eller tab af data, der kan skyldes brug af appen."
+ "Fortsæt"
+ "Indstillinger"
+ "Installerer/afinstallerer Wear-apps"
+
diff --git a/packages/PackageInstaller/res/values-de-television/strings.xml b/packages/PackageInstaller/res/values-de-television/strings.xml
new file mode 100644
index 0000000000000..dc218e443683d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Ablehnen und nicht mehr fragen"
+ "Du kannst dies später unter \"Einstellungen > Apps\" ändern."
+ "%1$s/%2$s"
+ "System-Apps anzeigen"
+ "App-Berechtigungen"
+ "App-Berechtigungen"
+ "Berechtigungen für %1$s"
+ "Zusätzliche Berechtigungen"
+ "Berechtigungen für %1$s"
+
diff --git a/packages/PackageInstaller/res/values-de-watch/strings.xml b/packages/PackageInstaller/res/values-de-watch/strings.xml
new file mode 100644
index 0000000000000..5a0b8e18f305c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Ablehnen & nicht mehr fragen"
+ "%1$s/%2$s"
+ "System-Apps anzeigen"
+ "Keine Änderung möglich"
+ "\"Ja\""
+ "Abbrechen"
+
diff --git a/packages/PackageInstaller/res/values-de/strings.xml b/packages/PackageInstaller/res/values-de/strings.xml
new file mode 100644
index 0000000000000..5a05c0cbeb92f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-de/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paket-Installer"
+ "Weiter"
+ "Installieren"
+ "Fertig"
+ "Abbrechen"
+ "Wird installiert..."
+ "%1$s wird installiert…"
+ "App wurde installiert."
+ "Möchtest du diese App installieren? Sie erhält dann folgende Berechtigungen:"
+ "Möchtest du diese App installieren? Sie benötigt keinen besonderen Zugriff."
+ "Möchtest du ein Update für diese vorhandene App installieren? Deine vorhandenen Daten bleiben erhalten. Die aktualisierte App erhält Zugriff auf:"
+ "Möchtest du ein Update für diese integrierte App installieren? Deine vorhandenen Daten bleiben erhalten. Die aktualisierte App erhält Zugriff auf:"
+ "Möchtest du ein Update für diese bestehende App installieren? Deine vorhandenen Daten bleiben erhalten. Die App benötigt keine besonderen Zugriffsrechte."
+ "Möchtest du ein Update für diese integrierte App installieren? Deine vorhandenen Daten bleiben erhalten. Die App benötigt keine besonderen Zugriffsrechte."
+ "App wurde nicht installiert."
+ "Die Installation des Pakets wurde blockiert."
+ "Die App wurde nicht installiert, da das Paket in Konflikt mit einem bestehenden Paket steht."
+ "Die App wurde nicht installiert, da sie nicht mit deinem Tablet kompatibel ist."
+ "Diese App ist nicht mit deinem Fernseher kompatibel."
+ "Die App wurde nicht installiert, da sie nicht mit deinem Smartphone kompatibel ist."
+ "Die App wurde nicht installiert, da das Paket offenbar ungültig ist."
+ "%1$s konnte nicht auf deinem Tablet installiert werden."
+ "%1$s konnte nicht auf deinem Fernseher installiert werden."
+ "%1$s konnte nicht auf deinem Telefon installiert werden."
+ "Öffnen"
+ "Dein Administrator lässt keine Installationen von Apps aus unbekannten Quellen zu"
+ "Dieser Nutzer darf keine unbekannten Apps installieren"
+ "Dieser Nutzer darf keine Apps installieren"
+ "OK"
+ "Apps verwalten"
+ "Kein freier Speicher vorhanden"
+ "%1$s konnte nicht installiert werden. Gib Speicherplatz frei und versuche es erneut."
+ "App nicht gefunden"
+ "Die App wurde nicht in der Liste der installierten Apps gefunden."
+ "Keine Berechtigung"
+ "Der aktuelle Nutzer ist nicht dazu berechtigt, diese Deinstallation auszuführen."
+ "Fehler"
+ "App konnte nicht deinstalliert werden."
+ "App deinstallieren"
+ "Update deinstallieren"
+ "%1$s gehört zu folgender App:"
+ "Möchtest du diese App deinstallieren?"
+ "Möchtest du diese App für ""alle"" Nutzer entfernen? Die App und alle zugehörigen Daten werden für ""alle"" Nutzer des Geräts entfernt."
+ "Möchtest du diese App für den Nutzer %1$s deinstallieren?"
+ "Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt."
+ "Diese App durch die Werksversion ersetzen? Alle Daten werden entfernt. Dies betrifft alle Nutzer des Geräts, einschließlich Arbeitsprofilen."
+ "Laufende Deinstallationen"
+ "Fehlgeschlagene Deinstallationen"
+ "Wird deinstalliert..."
+ "%1$s wird deinstalliert…"
+ "Deinstallation abgeschlossen"
+ "%1$s deinstalliert"
+ "Deinstallation fehlgeschlagen"
+ "Deinstallation von %1$s fehlgeschlagen."
+ "Aktive Apps zur Geräteverwaltung können nicht deinstalliert werden"
+ "Die aktive App zur Geräteverwaltung kann nicht für %1$s deinstalliert werden"
+ "Diese App wird für einige Nutzer oder Profile benötigt und wurde für andere deinstalliert"
+ "Diese App wird für dein Profil benötigt und kann nicht deinstalliert werden."
+ "Die App wurde als obligatorisch festgelegt und kann nicht deinstalliert werden."
+ "Apps zur Geräteverwaltung"
+ "Nutzer verwalten"
+ "%1$s konnte nicht deinstalliert werden."
+ "Beim Parsen des Pakets ist ein Problem aufgetreten."
+ "Neu"
+ "Alle"
+ "Datenschutz"
+ "Gerätezugriff"
+ "Für dieses Update sind keine neuen Berechtigungen erforderlich."
+ "Ablehnen"
+ "Weitere Informationen"
+ "Trotzdem ablehnen"
+ "%1$s von %2$s"
+ "Zulassen, dass die App <b>%1$s</b> %2$s darf?"
+ "<b>%1$s</b> immer erlauben zu %2$s?"
+ "Nur während der App-Nutzung"
+ "Immer"
+ "Ablehnen und nicht mehr fragen"
+ "%1$d deaktiviert"
+ "Alle deaktiviert"
+ "Keine deaktiviert"
+ "Zulassen"
+ "Apps"
+ "App-Berechtigungen"
+ "Nicht mehr fragen"
+ "Keine Berechtigungen"
+ "Zusätzliche Berechtigungen"
+ "App-Info öffnen"
+
+ Noch %1$d
+ Noch %1$d
+
+ "Diese App wurde für eine ältere Version von Android konzipiert. Wenn du keine Berechtigung erteilst, funktioniert die App möglicherweise nicht mehr ordnungsgemäß."
+ "Unbekannte Aktion durchführen"
+ "%1$d von %2$d Apps sind berechtigt."
+ "System-Apps anzeigen"
+ "System ausblenden"
+ "Keine Apps"
+ "Standorteinstellungen"
+ "%1$s ist ein Anbieter von Standortdiensten für dieses Gerät. Die Berechtigungen für den Zugriff auf deinen Standort kannst du in den Standorteinstellungen ändern."
+ "Wenn du diese Berechtigung deaktivierst, funktionieren grundlegende Funktionen deines Geräts möglicherweise nicht mehr ordnungsgemäß."
+ "Von Richtlinien durchgesetzt"
+ "Hintergrundzugriff aufgrund der Richtlinie deaktiviert"
+ "Hintergrundzugriff aufgrund der Richtlinie aktiviert"
+ "Vordergrundzugriff aufgrund der Richtlinie aktiviert"
+ "Durch den Administrator verwaltet"
+
+
+
+ "Immer"
+ "Nur während der App-Nutzung"
+ "Nie"
+ "Wird geladen…"
+ "Alle Berechtigungen"
+ "Andere App-Funktionen"
+ "Berechtigungsanfrage"
+ "Display-Overlay erkannt"
+ "Um diese Berechtigungseinstellung zu ändern, musst du zunächst das Display-Overlay über \"Einstellungen\" > \"Apps\" deaktivieren."
+ "Einstellungen öffnen"
+ "Android Wear"
+ "Von Android Wear nicht unterstützte Aktionen installieren/deinstallieren."
+ "Worauf darf die App <b>%1$s</b> zugreifen?"
+ "Die App <b>%1$s</b> wurde aktualisiert. Worauf darf diese App zugreifen?"
+ "Abbrechen"
+ "Weiter"
+ "Neue Berechtigungen"
+ "Aktuelle Berechtigungen"
+ "App wird vorbereitet…"
+ "Unbekannt"
+ "Aus Sicherheitsgründen kannst du auf dem Tablet keine unbekannten Apps aus dieser Quelle installieren."
+ "Aus Sicherheitsgründen kannst du auf dem Fernseher keine unbekannten Apps aus dieser Quelle installieren."
+ "Aus Sicherheitsgründen kannst du auf dem Smartphone keine unbekannten Apps aus dieser Quelle installieren."
+ "Unbekannte Apps können gefährlich für dein Smartphone und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Smartphone und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."
+ "Unbekannte Apps können gefährlich für dein Tablet und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Tablet und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."
+ "Unbekannte Apps können gefährlich für deinen Fernseher und deine personenbezogenen Daten sein. Indem du diese App installierst, erklärst du dich damit einverstanden, dass du die Verantwortung für alle Schäden an deinem Fernseher und jegliche Datenverluste trägst, die aus der Verwendung dieser App entstehen können."
+ "Weiter"
+ "Einstellungen"
+ "Wear-Apps installieren/deinstallieren"
+
diff --git a/packages/PackageInstaller/res/values-el-television/strings.xml b/packages/PackageInstaller/res/values-el-television/strings.xml
new file mode 100644
index 0000000000000..44f77a95dfe73
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Απόρριψη και να μην ερωτηθώ ξανά"
+ "Μπορείτε να το αλλάξετε αυτό αργότερα από το μενού Ρυθμίσεις > Εφαρμογές"
+ "%1$s / %2$s"
+ "Εμφάνιση εφαρμογών συστήματος"
+ "Άδειες εφαρμογών"
+ "Άδειες εφαρμογών"
+ "Άδειες - %1$s"
+ "Πρόσθετες άδειες"
+ "Άδειες - %1$s"
+
diff --git a/packages/PackageInstaller/res/values-el-watch/strings.xml b/packages/PackageInstaller/res/values-el-watch/strings.xml
new file mode 100644
index 0000000000000..3d923dcc8c8a5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Απόρριψη και να μην ερωτηθώ ξανά"
+ "%1$s / %2$s"
+ "Εμφάνιση εφαρμογών συστήματος"
+ "Δεν είναι δυνατή η αλλαγή"
+ "Ναι"
+ "Ακύρωση"
+
diff --git a/packages/PackageInstaller/res/values-el/strings.xml b/packages/PackageInstaller/res/values-el/strings.xml
new file mode 100644
index 0000000000000..707f7b1b82f11
--- /dev/null
+++ b/packages/PackageInstaller/res/values-el/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Πρόγραμμα εγκατάστασης πακέτου"
+ "Επόμενο"
+ "Εγκατάσταση"
+ "Τέλος"
+ "Ακύρωση"
+ "Εγκατάσταση..."
+ "Εγκατάσταση %1$s…"
+ "Εγκατάσταση εφαρμογής."
+ "Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή; Θα έχει πρόσβαση σε:"
+ "Θέλετε να εγκαταστήσετε αυτήν την εφαρμογή; Δεν απαιτείται οποιαδήποτε ειδική πρόσβαση."
+ "Θέλετε να εγκαταστήσετε μια ενημέρωση σε αυτήν την υπάρχουσα εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Η ενημερωμένη εφαρμογή θα έχει πρόσβαση σε:"
+ "Θέλετε να εγκαταστήσετε μια ενημέρωση σε αυτήν την ενσωματωμένη εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Η ενημερωμένη εφαρμογή θα έχει πρόσβαση σε:"
+ "Θέλετε να εγκαταστήσετε μια ενημέρωση για αυτήν την υπάρχουσα εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Δεν απαιτείται ειδική πρόσβαση."
+ "Θέλετε να εγκαταστήσετε μια ενημέρωση για αυτήν την ενσωματωμένη εφαρμογή; Τα υπάρχοντα δεδομένα σας δεν θα χαθούν. Δεν απαιτείται ειδική πρόσβαση."
+ "Η εφαρμογή δεν έχει εγκατασταθεί."
+ "Η εγκατάσταση του πακέτου αποκλείστηκε."
+ "Η εφαρμογή δεν έχει εγκατασταθεί, επειδή το πακέτο έρχεται σε διένεξη με κάποιο υπάρχον πακέτο."
+ "Η εφαρμογή δεν έχει εγκατασταθεί, επειδή δεν είναι συμβατή με το tablet που χρησιμοποιείτε."
+ "Αυτή η εφαρμογή δεν είναι συμβατή με την τηλεόρασή σας."
+ "Η εφαρμογή δεν έχει εγκατασταθεί, επειδή δεν είναι συμβατή με το τηλέφωνό σας."
+ "Η εφαρμογή δεν έχει εγκατασταθεί, επειδή φαίνεται ότι το πακέτο δεν είναι έγκυρο."
+ "Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής %1$s στο tablet σας."
+ "Η εφαρμογή %1$s δεν ήταν δυνατό να εγκατασταθεί στην τηλεόρασή σας."
+ "Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής %1$s στο τηλέφωνό σας."
+ "Άνοιγμα"
+ "Ο διαχειριστής σας δεν επιτρέπει την εγκατάσταση εφαρμογών που προέρχονται από άγνωστες πηγές"
+ "Δεν είναι δυνατή η εγκατάσταση άγνωστων εφαρμογών από αυτόν τον χρήστη"
+ "Αυτός ο χρήστης δεν έχει δυνατότητα εγκατάστασης εφαρμογών."
+ "OK"
+ "Διαχείριση εφαρμογών"
+ "Δεν υπάρχει χώρος"
+ "Δεν ήταν δυνατή η εγκατάσταση της εφαρμογής %1$s. Απελευθερώστε λίγο χώρο και προσπαθήστε ξανά."
+ "Δεν βρέθηκε εφαρμογή"
+ "Η εφαρμογή δεν βρέθηκε στη λίστα με τις εγκατεστημένες εφαρμογές."
+ "Δεν επιτρέπεται"
+ "Δεν επιτρέπεται στον τρέχοντα χρήση να εκτελέσει την απεγκατάσταση."
+ "Σφάλμα"
+ "Δεν ήταν δυνατή η απεγκατάσταση της εφαρμογής."
+ "Απεγκατάσταση εφαρμογής"
+ "Απεγκατάσταση ενημέρωσης"
+ "Η δραστηριότητα %1$s αποτελεί τμήμα της ακόλουθης εφαρμογής:"
+ "Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής;"
+ "Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής για ""όλους"" τους χρήστες; Η εφαρμογή και τα δεδομένα της θα καταργηθούν από ""όλους"" τους χρήστες στη συσκευή."
+ "Θέλετε να καταργήσετε την εγκατάσταση αυτής της εφαρμογής για το χρήστη %1$s;"
+ "Αντικατάσταση αυτής της εφαρμογής με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν."
+ "Αντικατάσταση αυτής της εφαρμογής με την εργοστασιακή έκδοση; Όλα τα δεδομένα θα καταργηθούν. Αυτό επηρεάζει όλους τους χρήστες της συσκευής, συμπεριλαμβανομένων και αυτών με προφίλ εργασίας."
+ "Απεγκαταστάσεις σε εξέλιξη"
+ "Αποτυχημένες απεγκαταστάσεις"
+ "Απεγκατάσταση..."
+ "Απεγκατάσταση %1$s…"
+ "Η κατάργηση εγκατάστασης ολοκληρώθηκε."
+ "Απεγκαταστάθηκε %1$s"
+ "Μη επιτυχής κατάργηση εγκατάστασης."
+ "Επιτυχής απεγκατάσταση %1$s."
+ "Η κατάργ. εγκατάστ. της ενεργούς εφαρμογής διαχείρισης συσκευής δεν είναι δυνατή"
+ "Η κατάργ. εγκατάστασης της ενεργούς εφαρμογής διαχείρισης συσκευής για τον χρήστη %1$s δεν είναι δυνατή"
+ "Η εφαρμογή απαιτείται για κάποιους χρήστες/προφίλ και απεγκαταστήθηκε για άλλους"
+ "Αυτή η εφαρμογή απαιτείται για το προφίλ σας και δεν είναι δυνατή η απεγκατάστασή της."
+ "Η εφαρμογή απαιτείται από το διαχειριστή και δεν είναι δυνατή η απεγκατάσταση."
+ "Διαχείριση εφαρμογών διαχείρισης συσκευής"
+ "Διαχείριση χρηστών"
+ "Δεν ήταν δυνατή η κατάργηση της εγκατάστασης της εφαρμογής %1$s."
+ "Παρουσιάστηκε ένα πρόβλημα κατά την ανάλυση του πακέτου."
+ "Νέο"
+ "Όλα"
+ "Απόρρητο"
+ "Πρόσβαση συσκευής"
+ "Αυτή η ενημέρωση δεν απαιτεί νέες άδειες."
+ "Απόρριψη"
+ "Περισσότερες πληροφορίες"
+ "Απόρριψη"
+ "%1$s από %2$s"
+ "Να επιτρέπεται στην εφαρμογή <b>%1$s</b> να %2$s;"
+ "Να επιτρέπεται πάντα στην εφαρμογή <b>%1$s</b> να %2$s;"
+ "Μόνο κατά τη χρήση της εφαρμογής"
+ "Πάντα"
+ "Απόρριψη και να μην ερωτηθώ ξανά"
+ "έχουν απενεργοποιηθεί %1$d"
+ "έχουν απενεργοποιηθεί όλες"
+ "δεν έχει απενεργοποιηθεί καμία"
+ "Να επιτρέπεται"
+ "Εφαρμογές"
+ "Δικαιώματα εφ/γών"
+ "Να μην ερωτηθώ ξανά"
+ "Χωρίς δικαιώματα"
+ "Πρόσθετα δικαιώματα"
+ "Άνοιγμα πληροφοριών εφαρμογής"
+
+ Ακόμα %1$d
+ Ακόμα %1$d
+
+ "Αυτή η εφαρμογή σχεδιάστηκε για παλαιότερη έκδοση του Android. Η άρνηση άδειας μπορεί να έχει ως αποτέλεσμα να διακοπεί η κανονική λειτουργία της."
+ "εκτέλεση άγνωστης ενέργειας"
+ "Επιτρέπονται %1$d από %2$d εφαρμογές"
+ "Εμφάνιση συστήματος"
+ "Απόκρυψη συστήματος"
+ "Δεν υπάρχουν εφαρμογές"
+ "Ρυθμίσεις τοποθεσίας"
+ "Η εφαρμογή %1$s είναι ο πάροχος των υπηρεσιών τοποθεσίας για τη συγκεκριμένη συσκευή. Μπορείτε να τροποποιήσετε την πρόσβαση τοποθεσίας από τις ρυθμίσεις τοποθεσίας."
+ "Εάν αρνηθείτε να παραχωρήσετε αυτήν την άδεια, η λειτουργία ορισμένων βασικών δυνατοτήτων ενδέχεται να μην είναι η αναμενόμενη."
+ "Επιβάλλεται από την πολιτική"
+ "Η πρόσβαση στο παρασκήνιο απενενεργοποιήθηκε βάσει πολιτικής"
+ "Η πρόσβαση στο παρασκήνιο ενεργοποιήθηκε βάσει πολιτικής"
+ "Η πρόσβαση στο προσκήνιο ενεργοποιήθηκε βάσει πολιτικής"
+ "Ελέγχεται από τον διαχειριστή"
+
+
+
+ "Πάντα"
+ "Μόνο κατά τη χρήση της εφαρμ."
+ "Ποτέ"
+ "Γίνεται φόρτωση…"
+ "Όλα τα δικαιώματα"
+ "Άλλες δυνατότητες εφαρμογής"
+ "Αίτημα άδειας"
+ "Εντοπίστηκε επικάλυψη οθόνης"
+ "Για να αλλάξετε αυτή τη ρύθμιση άδειας, θα πρέπει πρώτα να απενεργοποιήσετε την επικάλυψη οθόνης από τις Ρυθμίσεις > Εφαρμογές"
+ "Άνοιγμα ρυθμίσεων"
+ "Android Wear"
+ "Οι ενέργειες εγκατάστασης/απεγκατάστασης δεν υποστηρίζονται στο Wear."
+ "Επιλέξτε σε τι θα έχει πρόσβαση η εφαρμογή <b>%1$s</b>"
+ "Η εφαρμογή <b>%1$s</b> ενημερώθηκε. Επιλέξτε σε τι θα έχει πρόσβαση αυτή η εφαρμογή."
+ "Ακύρωση"
+ "Συνέχεια"
+ "Νέες άδειες"
+ "Τρέχουσες άδειες"
+ "Σταδιακή διάθεση εφαρμογής…"
+ "Άγνωστη"
+ "Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο tablet σας."
+ "Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στην τηλεόρασή σας."
+ "Για λόγους ασφάλειας, δεν επιτρέπεται η εγκατάσταση άγνωστων εφαρμογών από αυτήν την πηγή στο τηλέφωνό σας."
+ "Το τηλέφωνό σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στο τηλέφωνο ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."
+ "Το tablet σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στο tablet ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."
+ "Η τηλεόρασή σας και τα προσωπικά δεδομένα σας είναι πιο ευάλωτα σε επιθέσεις από άγνωστες εφαρμογές. Με την εγκατάσταση αυτής της εφαρμογής, συμφωνείτε ότι είστε υπεύθυνοι για οποιαδήποτε ζημιά στην τηλεόρασή ή απώλεια δεδομένων που μπορεί να προκύψει από τη χρήση τους."
+ "Συνέχεια"
+ "Ρυθμίσεις"
+ "Εγκατάσταση/απεγκατάσταση εφαρμογών Wear"
+
diff --git a/packages/PackageInstaller/res/values-en-rAU-television/strings.xml b/packages/PackageInstaller/res/values-en-rAU-television/strings.xml
new file mode 100644
index 0000000000000..663e1d4d0cb17
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Deny and don\'t ask again"
+ "You can change this later in Settings > Apps"
+ "%1$s / %2$s"
+ "Show system apps"
+ "App permissions"
+ "App permissions"
+ "%1$s permissions"
+ "Additional permissions"
+ "%1$s permissions"
+
diff --git a/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml b/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml
new file mode 100644
index 0000000000000..e0d0edbc5932a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Deny, don\'t ask again"
+ "%1$s / %2$s"
+ "Show system apps"
+ "Can\'t be changed"
+ "Yes"
+ "Cancel"
+
diff --git a/packages/PackageInstaller/res/values-en-rAU/strings.xml b/packages/PackageInstaller/res/values-en-rAU/strings.xml
new file mode 100644
index 0000000000000..ded57bbdc931b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rAU/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package installer"
+ "Next"
+ "Install"
+ "Done"
+ "Cancel"
+ "Installing…"
+ "Installing %1$s…"
+ "App installed."
+ "Do you want to install this application? It will get access to:"
+ "Do you want to install this application? It does not require any special access."
+ "Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."
+ "App not installed."
+ "The package was blocked from being installed."
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "This app isn\'t compatible with your TV."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "%1$s couldn\'t be installed on your tablet."
+ "%1$s couldn\'t be installed on your TV."
+ "%1$s couldn\'t be installed on your phone."
+ "Open"
+ "Your admin doesn\'t allow installation of apps obtained from unknown sources"
+ "Unknown apps can\'t be installed by this user"
+ "This user is not allowed to install apps"
+ "OK"
+ "Manage apps"
+ "Out of space"
+ "%1$s couldn\'t be installed. Free up some space and try again."
+ "App not found"
+ "The app wasn\'t found in the list of installed apps."
+ "Not allowed"
+ "The current user is not allowed to perform this uninstallation."
+ "Error"
+ "App could not be uninstalled."
+ "Uninstall app"
+ "Uninstall update"
+ "%1$s is part of the following app:"
+ "Do you want to uninstall this app?"
+ "Do you want to uninstall this app for ""all"" users? The application and its data will be removed from ""all"" users on the device."
+ "Do you want to uninstall this app for the user %1$s?"
+ "Replace this app with the factory version? All data will be removed."
+ "Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."
+ "Running uninstalls"
+ "Failed uninstalls"
+ "Uninstalling…"
+ "Uninstalling %1$s…"
+ "Uninstall finished"
+ "Uninstalled %1$s"
+ "Uninstall unsuccessful."
+ "Uninstalling %1$s unsuccessful."
+ "Can\'t uninstall active device admin app"
+ "Can\'t uninstall active device admin app for %1$s"
+ "This app is required for some users or profiles and was uninstalled for others"
+ "This app is needed for your profile and can\'t be uninstalled."
+ "This app is required by your device administrator and can\'t be uninstalled."
+ "Manage device admin apps"
+ "Manage users"
+ "%1$s couldn\'t be uninstalled."
+ "There was a problem while parsing the package."
+ "New"
+ "All"
+ "Privacy"
+ "Device Access"
+ "This update requires no new permissions."
+ "Deny"
+ "More info"
+ "Deny anyway"
+ "%1$s of %2$s"
+ "Allow <b>%1$s</b> to %2$s?"
+ "Always allow <b>%1$s</b> to %2$s?"
+ "Only while using app"
+ "Always"
+ "Deny and don’t ask again"
+ "%1$d disabled"
+ "all disabled"
+ "none disabled"
+ "Allow"
+ "Apps"
+ "App permissions"
+ "Don\'t ask again"
+ "No permissions"
+ "Additional permissions"
+ "Open app info"
+
+ %1$d more
+ %1$d more
+
+ "This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."
+ "perform an unknown action"
+ "%1$d of %2$d apps allowed"
+ "Show system"
+ "Hide system"
+ "No apps"
+ "Location Settings"
+ "%1$s is a provider of location services for this device. Location access can be modified from location settings."
+ "If you deny this permission, basic features of your device may no longer function as intended."
+ "Enforced by policy"
+ "Background access disabled by policy"
+ "Background access enabled by policy"
+ "Foreground access enabled by policy"
+ "Controlled by admin"
+
+
+
+ "Always"
+ "Only while using app"
+ "Never"
+ "Loading…"
+ "All permissions"
+ "Other app capabilities"
+ "Permission request"
+ "Screen overlay detected"
+ "To change this permission setting, you first have to turn off the screen overlay from Settings > Apps"
+ "Open settings"
+ "Android Wear"
+ "Install/Uninstall actions not supported on Wear."
+ "Choose what to allow <b>%1$s</b> to access"
+ "<b>%1$s</b> has been updated. Choose what to allow this app to access."
+ "Cancel"
+ "Continue"
+ "New permissions"
+ "Current permissions"
+ "Staging app…"
+ "Unknown"
+ "For your security, your tablet is not allowed to install unknown apps from this source."
+ "For your security, your TV is not allowed to install unknown apps from this source."
+ "For your security, your phone is not allowed to install unknown apps from this source."
+ "Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."
+ "Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."
+ "Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."
+ "Continue"
+ "Settings"
+ "Installing/uninstalling Wear apps"
+
diff --git a/packages/PackageInstaller/res/values-en-rCA-television/strings.xml b/packages/PackageInstaller/res/values-en-rCA-television/strings.xml
new file mode 100644
index 0000000000000..663e1d4d0cb17
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Deny and don\'t ask again"
+ "You can change this later in Settings > Apps"
+ "%1$s / %2$s"
+ "Show system apps"
+ "App permissions"
+ "App permissions"
+ "%1$s permissions"
+ "Additional permissions"
+ "%1$s permissions"
+
diff --git a/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml b/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml
new file mode 100644
index 0000000000000..e0d0edbc5932a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Deny, don\'t ask again"
+ "%1$s / %2$s"
+ "Show system apps"
+ "Can\'t be changed"
+ "Yes"
+ "Cancel"
+
diff --git a/packages/PackageInstaller/res/values-en-rCA/strings.xml b/packages/PackageInstaller/res/values-en-rCA/strings.xml
new file mode 100644
index 0000000000000..ded57bbdc931b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rCA/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package installer"
+ "Next"
+ "Install"
+ "Done"
+ "Cancel"
+ "Installing…"
+ "Installing %1$s…"
+ "App installed."
+ "Do you want to install this application? It will get access to:"
+ "Do you want to install this application? It does not require any special access."
+ "Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."
+ "App not installed."
+ "The package was blocked from being installed."
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "This app isn\'t compatible with your TV."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "%1$s couldn\'t be installed on your tablet."
+ "%1$s couldn\'t be installed on your TV."
+ "%1$s couldn\'t be installed on your phone."
+ "Open"
+ "Your admin doesn\'t allow installation of apps obtained from unknown sources"
+ "Unknown apps can\'t be installed by this user"
+ "This user is not allowed to install apps"
+ "OK"
+ "Manage apps"
+ "Out of space"
+ "%1$s couldn\'t be installed. Free up some space and try again."
+ "App not found"
+ "The app wasn\'t found in the list of installed apps."
+ "Not allowed"
+ "The current user is not allowed to perform this uninstallation."
+ "Error"
+ "App could not be uninstalled."
+ "Uninstall app"
+ "Uninstall update"
+ "%1$s is part of the following app:"
+ "Do you want to uninstall this app?"
+ "Do you want to uninstall this app for ""all"" users? The application and its data will be removed from ""all"" users on the device."
+ "Do you want to uninstall this app for the user %1$s?"
+ "Replace this app with the factory version? All data will be removed."
+ "Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."
+ "Running uninstalls"
+ "Failed uninstalls"
+ "Uninstalling…"
+ "Uninstalling %1$s…"
+ "Uninstall finished"
+ "Uninstalled %1$s"
+ "Uninstall unsuccessful."
+ "Uninstalling %1$s unsuccessful."
+ "Can\'t uninstall active device admin app"
+ "Can\'t uninstall active device admin app for %1$s"
+ "This app is required for some users or profiles and was uninstalled for others"
+ "This app is needed for your profile and can\'t be uninstalled."
+ "This app is required by your device administrator and can\'t be uninstalled."
+ "Manage device admin apps"
+ "Manage users"
+ "%1$s couldn\'t be uninstalled."
+ "There was a problem while parsing the package."
+ "New"
+ "All"
+ "Privacy"
+ "Device Access"
+ "This update requires no new permissions."
+ "Deny"
+ "More info"
+ "Deny anyway"
+ "%1$s of %2$s"
+ "Allow <b>%1$s</b> to %2$s?"
+ "Always allow <b>%1$s</b> to %2$s?"
+ "Only while using app"
+ "Always"
+ "Deny and don’t ask again"
+ "%1$d disabled"
+ "all disabled"
+ "none disabled"
+ "Allow"
+ "Apps"
+ "App permissions"
+ "Don\'t ask again"
+ "No permissions"
+ "Additional permissions"
+ "Open app info"
+
+ %1$d more
+ %1$d more
+
+ "This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."
+ "perform an unknown action"
+ "%1$d of %2$d apps allowed"
+ "Show system"
+ "Hide system"
+ "No apps"
+ "Location Settings"
+ "%1$s is a provider of location services for this device. Location access can be modified from location settings."
+ "If you deny this permission, basic features of your device may no longer function as intended."
+ "Enforced by policy"
+ "Background access disabled by policy"
+ "Background access enabled by policy"
+ "Foreground access enabled by policy"
+ "Controlled by admin"
+
+
+
+ "Always"
+ "Only while using app"
+ "Never"
+ "Loading…"
+ "All permissions"
+ "Other app capabilities"
+ "Permission request"
+ "Screen overlay detected"
+ "To change this permission setting, you first have to turn off the screen overlay from Settings > Apps"
+ "Open settings"
+ "Android Wear"
+ "Install/Uninstall actions not supported on Wear."
+ "Choose what to allow <b>%1$s</b> to access"
+ "<b>%1$s</b> has been updated. Choose what to allow this app to access."
+ "Cancel"
+ "Continue"
+ "New permissions"
+ "Current permissions"
+ "Staging app…"
+ "Unknown"
+ "For your security, your tablet is not allowed to install unknown apps from this source."
+ "For your security, your TV is not allowed to install unknown apps from this source."
+ "For your security, your phone is not allowed to install unknown apps from this source."
+ "Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."
+ "Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."
+ "Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."
+ "Continue"
+ "Settings"
+ "Installing/uninstalling Wear apps"
+
diff --git a/packages/PackageInstaller/res/values-en-rGB-television/strings.xml b/packages/PackageInstaller/res/values-en-rGB-television/strings.xml
new file mode 100644
index 0000000000000..663e1d4d0cb17
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Deny and don\'t ask again"
+ "You can change this later in Settings > Apps"
+ "%1$s / %2$s"
+ "Show system apps"
+ "App permissions"
+ "App permissions"
+ "%1$s permissions"
+ "Additional permissions"
+ "%1$s permissions"
+
diff --git a/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml b/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml
new file mode 100644
index 0000000000000..e0d0edbc5932a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Deny, don\'t ask again"
+ "%1$s / %2$s"
+ "Show system apps"
+ "Can\'t be changed"
+ "Yes"
+ "Cancel"
+
diff --git a/packages/PackageInstaller/res/values-en-rGB/strings.xml b/packages/PackageInstaller/res/values-en-rGB/strings.xml
new file mode 100644
index 0000000000000..ded57bbdc931b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rGB/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package installer"
+ "Next"
+ "Install"
+ "Done"
+ "Cancel"
+ "Installing…"
+ "Installing %1$s…"
+ "App installed."
+ "Do you want to install this application? It will get access to:"
+ "Do you want to install this application? It does not require any special access."
+ "Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."
+ "App not installed."
+ "The package was blocked from being installed."
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "This app isn\'t compatible with your TV."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "%1$s couldn\'t be installed on your tablet."
+ "%1$s couldn\'t be installed on your TV."
+ "%1$s couldn\'t be installed on your phone."
+ "Open"
+ "Your admin doesn\'t allow installation of apps obtained from unknown sources"
+ "Unknown apps can\'t be installed by this user"
+ "This user is not allowed to install apps"
+ "OK"
+ "Manage apps"
+ "Out of space"
+ "%1$s couldn\'t be installed. Free up some space and try again."
+ "App not found"
+ "The app wasn\'t found in the list of installed apps."
+ "Not allowed"
+ "The current user is not allowed to perform this uninstallation."
+ "Error"
+ "App could not be uninstalled."
+ "Uninstall app"
+ "Uninstall update"
+ "%1$s is part of the following app:"
+ "Do you want to uninstall this app?"
+ "Do you want to uninstall this app for ""all"" users? The application and its data will be removed from ""all"" users on the device."
+ "Do you want to uninstall this app for the user %1$s?"
+ "Replace this app with the factory version? All data will be removed."
+ "Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."
+ "Running uninstalls"
+ "Failed uninstalls"
+ "Uninstalling…"
+ "Uninstalling %1$s…"
+ "Uninstall finished"
+ "Uninstalled %1$s"
+ "Uninstall unsuccessful."
+ "Uninstalling %1$s unsuccessful."
+ "Can\'t uninstall active device admin app"
+ "Can\'t uninstall active device admin app for %1$s"
+ "This app is required for some users or profiles and was uninstalled for others"
+ "This app is needed for your profile and can\'t be uninstalled."
+ "This app is required by your device administrator and can\'t be uninstalled."
+ "Manage device admin apps"
+ "Manage users"
+ "%1$s couldn\'t be uninstalled."
+ "There was a problem while parsing the package."
+ "New"
+ "All"
+ "Privacy"
+ "Device Access"
+ "This update requires no new permissions."
+ "Deny"
+ "More info"
+ "Deny anyway"
+ "%1$s of %2$s"
+ "Allow <b>%1$s</b> to %2$s?"
+ "Always allow <b>%1$s</b> to %2$s?"
+ "Only while using app"
+ "Always"
+ "Deny and don’t ask again"
+ "%1$d disabled"
+ "all disabled"
+ "none disabled"
+ "Allow"
+ "Apps"
+ "App permissions"
+ "Don\'t ask again"
+ "No permissions"
+ "Additional permissions"
+ "Open app info"
+
+ %1$d more
+ %1$d more
+
+ "This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."
+ "perform an unknown action"
+ "%1$d of %2$d apps allowed"
+ "Show system"
+ "Hide system"
+ "No apps"
+ "Location Settings"
+ "%1$s is a provider of location services for this device. Location access can be modified from location settings."
+ "If you deny this permission, basic features of your device may no longer function as intended."
+ "Enforced by policy"
+ "Background access disabled by policy"
+ "Background access enabled by policy"
+ "Foreground access enabled by policy"
+ "Controlled by admin"
+
+
+
+ "Always"
+ "Only while using app"
+ "Never"
+ "Loading…"
+ "All permissions"
+ "Other app capabilities"
+ "Permission request"
+ "Screen overlay detected"
+ "To change this permission setting, you first have to turn off the screen overlay from Settings > Apps"
+ "Open settings"
+ "Android Wear"
+ "Install/Uninstall actions not supported on Wear."
+ "Choose what to allow <b>%1$s</b> to access"
+ "<b>%1$s</b> has been updated. Choose what to allow this app to access."
+ "Cancel"
+ "Continue"
+ "New permissions"
+ "Current permissions"
+ "Staging app…"
+ "Unknown"
+ "For your security, your tablet is not allowed to install unknown apps from this source."
+ "For your security, your TV is not allowed to install unknown apps from this source."
+ "For your security, your phone is not allowed to install unknown apps from this source."
+ "Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."
+ "Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."
+ "Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."
+ "Continue"
+ "Settings"
+ "Installing/uninstalling Wear apps"
+
diff --git a/packages/PackageInstaller/res/values-en-rIN-television/strings.xml b/packages/PackageInstaller/res/values-en-rIN-television/strings.xml
new file mode 100644
index 0000000000000..663e1d4d0cb17
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Deny and don\'t ask again"
+ "You can change this later in Settings > Apps"
+ "%1$s / %2$s"
+ "Show system apps"
+ "App permissions"
+ "App permissions"
+ "%1$s permissions"
+ "Additional permissions"
+ "%1$s permissions"
+
diff --git a/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml b/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml
new file mode 100644
index 0000000000000..e0d0edbc5932a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Deny, don\'t ask again"
+ "%1$s / %2$s"
+ "Show system apps"
+ "Can\'t be changed"
+ "Yes"
+ "Cancel"
+
diff --git a/packages/PackageInstaller/res/values-en-rIN/strings.xml b/packages/PackageInstaller/res/values-en-rIN/strings.xml
new file mode 100644
index 0000000000000..ded57bbdc931b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rIN/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package installer"
+ "Next"
+ "Install"
+ "Done"
+ "Cancel"
+ "Installing…"
+ "Installing %1$s…"
+ "App installed."
+ "Do you want to install this application? It will get access to:"
+ "Do you want to install this application? It does not require any special access."
+ "Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."
+ "App not installed."
+ "The package was blocked from being installed."
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "This app isn\'t compatible with your TV."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "%1$s couldn\'t be installed on your tablet."
+ "%1$s couldn\'t be installed on your TV."
+ "%1$s couldn\'t be installed on your phone."
+ "Open"
+ "Your admin doesn\'t allow installation of apps obtained from unknown sources"
+ "Unknown apps can\'t be installed by this user"
+ "This user is not allowed to install apps"
+ "OK"
+ "Manage apps"
+ "Out of space"
+ "%1$s couldn\'t be installed. Free up some space and try again."
+ "App not found"
+ "The app wasn\'t found in the list of installed apps."
+ "Not allowed"
+ "The current user is not allowed to perform this uninstallation."
+ "Error"
+ "App could not be uninstalled."
+ "Uninstall app"
+ "Uninstall update"
+ "%1$s is part of the following app:"
+ "Do you want to uninstall this app?"
+ "Do you want to uninstall this app for ""all"" users? The application and its data will be removed from ""all"" users on the device."
+ "Do you want to uninstall this app for the user %1$s?"
+ "Replace this app with the factory version? All data will be removed."
+ "Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."
+ "Running uninstalls"
+ "Failed uninstalls"
+ "Uninstalling…"
+ "Uninstalling %1$s…"
+ "Uninstall finished"
+ "Uninstalled %1$s"
+ "Uninstall unsuccessful."
+ "Uninstalling %1$s unsuccessful."
+ "Can\'t uninstall active device admin app"
+ "Can\'t uninstall active device admin app for %1$s"
+ "This app is required for some users or profiles and was uninstalled for others"
+ "This app is needed for your profile and can\'t be uninstalled."
+ "This app is required by your device administrator and can\'t be uninstalled."
+ "Manage device admin apps"
+ "Manage users"
+ "%1$s couldn\'t be uninstalled."
+ "There was a problem while parsing the package."
+ "New"
+ "All"
+ "Privacy"
+ "Device Access"
+ "This update requires no new permissions."
+ "Deny"
+ "More info"
+ "Deny anyway"
+ "%1$s of %2$s"
+ "Allow <b>%1$s</b> to %2$s?"
+ "Always allow <b>%1$s</b> to %2$s?"
+ "Only while using app"
+ "Always"
+ "Deny and don’t ask again"
+ "%1$d disabled"
+ "all disabled"
+ "none disabled"
+ "Allow"
+ "Apps"
+ "App permissions"
+ "Don\'t ask again"
+ "No permissions"
+ "Additional permissions"
+ "Open app info"
+
+ %1$d more
+ %1$d more
+
+ "This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."
+ "perform an unknown action"
+ "%1$d of %2$d apps allowed"
+ "Show system"
+ "Hide system"
+ "No apps"
+ "Location Settings"
+ "%1$s is a provider of location services for this device. Location access can be modified from location settings."
+ "If you deny this permission, basic features of your device may no longer function as intended."
+ "Enforced by policy"
+ "Background access disabled by policy"
+ "Background access enabled by policy"
+ "Foreground access enabled by policy"
+ "Controlled by admin"
+
+
+
+ "Always"
+ "Only while using app"
+ "Never"
+ "Loading…"
+ "All permissions"
+ "Other app capabilities"
+ "Permission request"
+ "Screen overlay detected"
+ "To change this permission setting, you first have to turn off the screen overlay from Settings > Apps"
+ "Open settings"
+ "Android Wear"
+ "Install/Uninstall actions not supported on Wear."
+ "Choose what to allow <b>%1$s</b> to access"
+ "<b>%1$s</b> has been updated. Choose what to allow this app to access."
+ "Cancel"
+ "Continue"
+ "New permissions"
+ "Current permissions"
+ "Staging app…"
+ "Unknown"
+ "For your security, your tablet is not allowed to install unknown apps from this source."
+ "For your security, your TV is not allowed to install unknown apps from this source."
+ "For your security, your phone is not allowed to install unknown apps from this source."
+ "Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."
+ "Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."
+ "Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."
+ "Continue"
+ "Settings"
+ "Installing/uninstalling Wear apps"
+
diff --git a/packages/PackageInstaller/res/values-en-rXC-television/strings.xml b/packages/PackageInstaller/res/values-en-rXC-television/strings.xml
new file mode 100644
index 0000000000000..0f9eeb18ac2dd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Deny and don\'t ask again"
+ "You can change this later in Settings > Apps"
+ "%1$s / %2$s"
+ "Show system apps"
+ "App permissions"
+ "App permissions"
+ "%1$s permissions"
+ "Additional permissions"
+ "%1$s permissions"
+
diff --git a/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml b/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml
new file mode 100644
index 0000000000000..89b2ea23ad4d6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Deny, don\'t ask again"
+ "%1$s / %2$s"
+ "Show system apps"
+ "Can\'t be changed"
+ "Yes"
+ "Cancel"
+
diff --git a/packages/PackageInstaller/res/values-en-rXC/strings.xml b/packages/PackageInstaller/res/values-en-rXC/strings.xml
new file mode 100644
index 0000000000000..ca392cc39ef6b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-en-rXC/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package installer"
+ "Next"
+ "Install"
+ "Done"
+ "Cancel"
+ "Installing…"
+ "Installing %1$s…"
+ "App installed."
+ "Do you want to install this application? It will get access to:"
+ "Do you want to install this application? It does not require any special access."
+ "Do you want to install an update to this existing application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. The updated application will get access to:"
+ "Do you want to install an update to this existing application? Your existing data will not be lost. It does not require any special access."
+ "Do you want to install an update to this built-in application? Your existing data will not be lost. It does not require any special access."
+ "App not installed."
+ "The package was blocked from being installed."
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "This app isn\'t compatible with your TV."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "%1$s couldn\'t be installed on your tablet."
+ "%1$s couldn\'t be installed on your TV."
+ "%1$s couldn\'t be installed on your phone."
+ "Open"
+ "Your admin doesn\'t allow installation of apps obtained from unknown sources"
+ "Unknown apps can\'t be installed by this user"
+ "This user is not allowed to install apps"
+ "OK"
+ "Manage apps"
+ "Out of space"
+ "%1$s couldn\'t be installed. Free up some space and try again."
+ "App not found"
+ "The app wasn\'t found in the list of installed apps."
+ "Not allowed"
+ "The current user is not allowed to perform this uninstallation."
+ "Error"
+ "App could not be uninstalled."
+ "Uninstall app"
+ "Uninstall update"
+ "%1$s is part of the following app:"
+ "Do you want to uninstall this app?"
+ "Do you want to uninstall this app for ""all"" users? The application and its data will be removed from ""all"" users on the device."
+ "Do you want to uninstall this app for the user %1$s?"
+ "Replace this app with the factory version? All data will be removed."
+ "Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles."
+ "Running uninstalls"
+ "Failed uninstalls"
+ "Uninstalling…"
+ "Uninstalling %1$s…"
+ "Uninstall finished."
+ "Uninstalled %1$s"
+ "Uninstall unsuccessful."
+ "Uninstalling %1$s unsuccessful."
+ "Can\'t uninstall active device admin app"
+ "Can\'t uninstall active device admin app for %1$s"
+ "This app is required for some users or profiles and was uninstalled for others"
+ "This app is needed for your profile and can\'t be uninstalled."
+ "This app is required by your device administrator and can\'t be uninstalled."
+ "Manage device admin apps"
+ "Manage users"
+ "%1$s couldn\'t be uninstalled."
+ "There was a problem parsing the package."
+ "New"
+ "All"
+ "Privacy"
+ "Device Access"
+ "This update requires no new permissions."
+ "Deny"
+ "More info"
+ "Deny anyway"
+ "%1$s of %2$s"
+ "Allow <b>%1$s</b> to %2$s?"
+ "Always allow <b>%1$s</b> to %2$s?"
+ "Only while using app"
+ "Always"
+ "Deny and don’t ask again"
+ "%1$d disabled"
+ "all disabled"
+ "none disabled"
+ "Allow"
+ "Apps"
+ "App permissions"
+ "Don\'t ask again"
+ "No permissions"
+ "Additional permissions"
+ "Open app info"
+
+ %1$d more
+ %1$d more
+
+ "This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended."
+ "perform an unknown action"
+ "%1$d of %2$d apps allowed"
+ "Show system"
+ "Hide system"
+ "No apps"
+ "Location Settings"
+ "%1$s is a provider of location services for this device. Location access can be modified from location settings."
+ "If you deny this permission, basic features of your device may no longer function as intended."
+ "Enforced by policy"
+ "Background access disabled by policy"
+ "Background access enabled by policy"
+ "Foreground access enabled by policy"
+ "Controlled by admin"
+
+
+
+ "Always"
+ "Only while using app"
+ "Never"
+ "Loading…"
+ "All permissions"
+ "Other app capabilities"
+ "Permission request"
+ "Screen overlay detected"
+ "To change this permission setting, you first have to turn off the screen overlay from Settings > Apps"
+ "Open settings"
+ "Android Wear"
+ "Install/Uninstall actions not supported on Wear."
+ "Choose what to allow <b>%1$s</b> to access"
+ "<b>%1$s</b> has been updated. Choose what to allow this app to access."
+ "Cancel"
+ "Continue"
+ "New permissions"
+ "Current permissions"
+ "Staging app…"
+ "Unknown"
+ "For your security, your tablet is not allowed to install unknown apps from this source."
+ "For your security, your TV is not allowed to install unknown apps from this source."
+ "For your security, your phone is not allowed to install unknown apps from this source."
+ "Your phone and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your phone or loss of data that may result from its use."
+ "Your tablet and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your tablet or loss of data that may result from its use."
+ "Your TV and personal data are more vulnerable to attack by unknown apps. By installing this app, you agree that you are responsible for any damage to your TV or loss of data that may result from its use."
+ "Continue"
+ "Settings"
+ "Installing/uninstalling wear apps"
+
diff --git a/packages/PackageInstaller/res/values-es-rUS-television/strings.xml b/packages/PackageInstaller/res/values-es-rUS-television/strings.xml
new file mode 100644
index 0000000000000..e664dc2467359
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Denegar el permiso y no volver a preguntar"
+ "Puedes cambiar esta opción más tarde en Configuración > Aplicaciones"
+ "%1$s/%2$s"
+ "Mostrar apps del sistema"
+ "Permisos de apps"
+ "Permisos de apps"
+ "Permisos de %1$s"
+ "Permisos adicionales"
+ "Permisos de %1$s"
+
diff --git a/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml b/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml
new file mode 100644
index 0000000000000..388089216c03a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Denegar y no preguntar más"
+ "%1$s/%2$s"
+ "Mostrar apps del sistema"
+ "Inalterable"
+ "Sí"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-es-rUS/strings.xml b/packages/PackageInstaller/res/values-es-rUS/strings.xml
new file mode 100644
index 0000000000000..178f5517376b5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-rUS/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Programa de instalación del paquete"
+ "Siguiente"
+ "Instalar"
+ "Finalizado"
+ "Cancelar"
+ "Instalando…"
+ "Instalando %1$s…"
+ "Se instaló la aplicación."
+ "¿Deseas instalar la aplicación? Esta tendrá acceso a lo siguiente:"
+ "¿Deseas instalar esta aplicación? No requiere accesos especiales."
+ "¿Deseas instalar una actualización para esta aplicación? Tus datos no se perderán. La aplicación actualizada tendrá acceso a lo siguiente:"
+ "¿Deseas instalar una actualización para esta aplicación integrada? Tus datos no se perderán. La aplicación actualizada tendrá acceso a lo siguiente:"
+ "¿Quieres instalar una actualización de esta aplicación existente? Los datos existentes no se perderán. No se requiere ningún acceso especial."
+ "¿Quieres instalar una actualización de esta aplicación integrada? Los datos existentes no se perderán. No se requiere ningún acceso especial."
+ "No se instaló la aplicación."
+ "Se bloqueó el paquete para impedir la instalación."
+ "No se instaló la app ya que está en conflicto con un paquete existente."
+ "No se instaló la app porque no es compatible con tu tablet."
+ "Esta app no es compatible con la TV."
+ "No se instaló la app porque no es compatible con tu teléfono."
+ "No se instaló la app porque parece que el paquete no es válido."
+ "No se pudo instalar %1$s en tu tablet."
+ "No se pudo instalar %1$s en la TV."
+ "No se pudo instalar %1$s en tu dispositivo."
+ "Abrir"
+ "Tu administrador no permite la instalación de apps que se obtuvieron de fuentes desconocidas"
+ "Este usuario no puede instalar apps desconocidas"
+ "Este usuario no puede instalar apps"
+ "Aceptar"
+ "Administrar aplicaciones"
+ "Sin espacio"
+ "No se pudo instalar %1$s. Libera espacio y vuelve a intentarlo."
+ "No se encontró la aplicación."
+ "La aplicación no se encontró en la lista de aplicaciones instaladas."
+ "No tiene permiso"
+ "El usuario actual no tiene permiso para llevar a cabo esta desinstalación."
+ "Error"
+ "No se pudo desinstalar la app."
+ "Desinstalar la aplicación"
+ "Desinstalar la actualización"
+ "%1$s es parte de la siguiente aplicación:"
+ "¿Deseas desinstalar esta aplicación?"
+ "¿Quieres desinstalar esta aplicación para ""todos"" los usuarios? La aplicación y sus datos se eliminarán de ""todos"" los usuarios del dispositivo."
+ "¿Deseas desinstalar esta aplicación para el usuario %1$s?"
+ "¿Deseas reemplazar esta app con la versión de fábrica? Se quitarán todos los datos."
+ "¿Deseas reemplazar esta app con la versión de fábrica? Se quitarán todos los datos. Esta acción afectará a todos los usuarios de este dispositivo, incluidos los que poseen perfiles de trabajo."
+ "Desinstalaciones activas"
+ "Desinstalaciones con errores"
+ "Desinstalando…"
+ "Desinstalando %1$s…"
+ "La desinstalación finalizó."
+ "Se desinstaló %1$s"
+ "Desinstalación incorrecta"
+ "No se pudo desinstalar %1$s."
+ "No se puede desinstalar la app de administración activa del dispositivo"
+ "No se puede desinstalar la app de administración activa del dispositivo para %1$s"
+ "App necesaria en algunos usuarios o perfiles, y desinstalada en otros"
+ "Esta app es necesaria en tu perfil y no la puedes desinstalar."
+ "El admin. del dispositivo necesita esta aplicación y no se puede desinstalar."
+ "Administrar apps del dispositivo del administrador"
+ "Administrar usuarios"
+ "No se pudo desinstalar %1$s."
+ "Se produjo un error durante el análisis del paquete."
+ "Nuevo"
+ "Todo"
+ "Privacidad"
+ "Acceso al dispositivo"
+ "Esta actualización no requiere permisos nuevos."
+ "Rechazar"
+ "Más información"
+ "Denegar de todos modos"
+ "%1$s de %2$s"
+ "¿Quieres que la app de <b>%1$s</b> realice la siguiente acción: %2$s?"
+ "¿Permitir que <b>%1$s</b> pueda %2$s siempre?"
+ "Solo cuando se usa la app"
+ "Siempre"
+ "Denegar el permiso y no volver a preguntar"
+ "%1$d inhabilitados"
+ "todos inhabilitados"
+ "ninguno inhabilitado"
+ "Permitir"
+ "Aplicaciones"
+ "Permisos de apps"
+ "No volver a preguntar"
+ "Sin permisos"
+ "Permisos adicionales"
+ "Abrir información de la app"
+
+ %1$d más
+ %1$d más
+
+ "Esta aplicación se diseñó para una versión de Android anterior. Si deniegas el permiso, es posible que deje de funcionar de la forma prevista."
+ "realizar una acción desconocida"
+ "Se otorgó el permiso a %1$d de %2$d aplicaciones."
+ "Mostrar sistema"
+ "Ocultar sistema"
+ "Ninguna aplicación"
+ "Configuración de la ubicación"
+ "La aplicación %1$s provee servicios de ubicación a este dispositivo. El acceso a la ubicación puede modificarse desde la configuración de la ubicación."
+ "Si no concedes este permiso, es posible que algunas funciones básicas del dispositivo dejen de funcionar correctamente."
+ "Se aplica en función de la política"
+ "Acceso en segundo plano inhabilitado por la política"
+ "Acceso en segundo plano habilitado por la política"
+ "Acceso en primer plano habilitado por la política"
+ "Controlado por el administrador"
+
+
+
+ "Siempre"
+ "Solo cuando se usa la app"
+ "Nunca"
+ "Cargando…"
+ "Todos los permisos"
+ "Otras funciones de la aplicación"
+ "Solicitud de permiso"
+ "Se detectó una superposición de pantalla"
+ "Para cambiar esta configuración de permisos, primero debes desactivar la superposición de pantalla en Configuración > Aplicaciones"
+ "Abrir configuración"
+ "Android Wear"
+ "Wear no admite las acciones de instalación y desinstalación"
+ "Selecciona los permisos de acceso para <b>%1$s</b>"
+ "Se actualizó <b>%1$s</b>. Selecciona los permisos de acceso para esta app."
+ "Cancelar"
+ "Continuar"
+ "Permisos nuevos"
+ "Permisos actuales"
+ "Preparando app…"
+ "Desconocido"
+ "Por tu seguridad, tu tablet no tiene permitido instalar apps desconocidas de esta fuente."
+ "Por tu seguridad, tu TV no tiene permitido instalar apps desconocidas de esta fuente."
+ "Por tu seguridad, tu teléfono no tiene permitido instalar apps desconocidas de esta fuente."
+ "Tu teléfono y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu teléfono y la pérdida de datos que pueda ocasionar su uso."
+ "Tu tablet y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu tablet y la pérdida de datos que pueda ocasionar su uso."
+ "Tu TV y tus datos personales son más vulnerables a los ataques de apps desconocidas. Si instalas esta app, serás responsable de los daños que sufra tu TV y la pérdida de datos que pueda ocasionar su uso."
+ "Continuar"
+ "Configuración"
+ "Instalando/desinstalando apps para Wear"
+
diff --git a/packages/PackageInstaller/res/values-es-television/strings.xml b/packages/PackageInstaller/res/values-es-television/strings.xml
new file mode 100644
index 0000000000000..6a120630bcdf1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Denegar y no volver a preguntar"
+ "Puedes cambiar esta opción más tarde en Ajustes > Aplicaciones."
+ "%1$s/%2$s"
+ "Mostrar aplicaciones del sistema"
+ "Permisos de aplicaciones"
+ "Permisos de aplicaciones"
+ "Permisos: %1$s"
+ "Permisos adicionales"
+ "Permisos: %1$s"
+
diff --git a/packages/PackageInstaller/res/values-es-watch/strings.xml b/packages/PackageInstaller/res/values-es-watch/strings.xml
new file mode 100644
index 0000000000000..b3c4ff1256f18
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Denegar y no preguntar más"
+ "%1$s/%2$s"
+ "Mostrar aplicaciones del sistema"
+ "No se puede cambiar"
+ "Sí"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-es/strings.xml b/packages/PackageInstaller/res/values-es/strings.xml
new file mode 100644
index 0000000000000..bffaa0f8aa9ce
--- /dev/null
+++ b/packages/PackageInstaller/res/values-es/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instalador de paquetes"
+ "Siguiente"
+ "Instalar"
+ "Listo"
+ "Cancelar"
+ "Instalando…"
+ "Instalando %1$s…"
+ "Aplicación instalada"
+ "¿Quieres instalar esta aplicación? Tendrá los siguientes permisos:"
+ "¿Quieres instalar esta aplicación? No requiere accesos especiales."
+ "¿Quieres instalar una actualización de la aplicación? Tus datos no se perderán. La aplicación actualizada podrá acceder a:"
+ "¿Quieres instalar una actualización de esta aplicación integrada? Tus datos no se perderán. La aplicación actualizada podrá acceder a:"
+ "¿Quieres instalar una actualización de esta aplicación? Tus datos no se perderán. No requiere ningún acceso especial."
+ "¿Quieres instalar una actualización de esta aplicación integrada? Tus datos no se perderán. No requiere ningún acceso especial."
+ "Aplicación no instalada"
+ "Se ha bloqueado la instalación del paquete."
+ "La aplicación no se ha instalado debido a un conflicto con un paquete actual."
+ "La aplicación no se ha instalado porque no es compatible con tu tablet."
+ "Esta aplicación no es compatible con tu TV."
+ "La aplicación no se ha instalado porque no es compatible con tu teléfono."
+ "La aplicación no se ha instalado porque parece que el paquete no es válido."
+ "No se ha podido instalar %1$s en el tablet."
+ "%1$s no se ha podido instalar en tu TV."
+ "No se ha podido instalar %1$s en el teléfono."
+ "Abrir"
+ "El administrador no permite instalar aplicaciones de fuentes desconocidas"
+ "Este usuario no puede instalar aplicaciones desconocidas"
+ "Este usuario no tiene permiso para instalar aplicaciones"
+ "Aceptar"
+ "Administrar aplicaciones"
+ "Sin espacio"
+ "No se ha podido instalar la aplicación %1$s. Libera espacio y vuelve a intentarlo."
+ "Aplicación no encontrada"
+ "No se ha encontrado la aplicación en la lista de aplicaciones instaladas."
+ "No permitido"
+ "El usuario actual no puede iniciar el proceso de desinstalación."
+ "Error"
+ "No se ha podido desinstalar la aplicación."
+ "Desinstalar aplicación"
+ "Desinstalar actualización"
+ "%1$s forma parte de esta aplicación:"
+ "¿Quieres desinstalar esta aplicación?"
+ "¿Quieres desinstalar esta aplicación para ""todos"" los usuarios? La aplicación y sus datos se eliminarán de ""todos"" los usuarios del dispositivo."
+ "¿Quieres desinstalar esta aplicación para el usuario %1$s?"
+ "¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se eliminarán todos los datos."
+ "¿Quieres sustituir esta aplicación con la versión de fábrica? Ten en cuenta que se eliminarán todos los datos. Esto afecta a todos los usuarios del dispositivo, incluidos los que tienen perfiles de trabajo."
+ "Desinstalaciones en curso"
+ "Desinstalaciones fallidas"
+ "Desinstalando..."
+ "Desinstalando %1$s…"
+ "Desinstalación completada"
+ "Se ha desinstalado %1$s"
+ "Desinstalación correcta"
+ "No se ha podido desinstalar %1$s."
+ "No se puede desinstalar la aplicación de administración de dispositivos activa"
+ "No se ha podido desinstalar la aplicación de administración de dispositivos activa de %1$s"
+ "Aplicación necesaria para algunos usuarios o perfiles y desinstalada en otros casos"
+ "Esta aplicación es necesaria para tu perfil y no se puede desinstalar."
+ "Esta aplicación es necesaria para el administrador de tu dispositivo y no se puede desinstalar."
+ "Gestionar aplicaciones de admón. de dispositivos"
+ "Administrar usuarios"
+ "No se ha podido desinstalar %1$s."
+ "Se ha producido un error al analizar el paquete."
+ "Nuevo"
+ "Todos"
+ "Privacidad"
+ "Acceso al dispositivo"
+ "Esta actualización no requiere permisos nuevos."
+ "Denegar"
+ "Más información"
+ "Denegar de todos modos"
+ "%1$s de %2$s"
+ "¿Permitir a <b>%1$s</b> %2$s?"
+ "¿Quieres permitir siempre que <b>%1$s</b> %2$s?"
+ "Solo mientras se usa la aplicación"
+ "Siempre"
+ "Denegar y no volver a preguntar"
+ "Inhabilitados: %1$d"
+ "todos inhabilitados"
+ "ninguno inhabilitado"
+ "Permitir"
+ "Aplicaciones"
+ "Permisos de aplicaciones"
+ "No volver a preguntar"
+ "Sin permisos"
+ "Permisos adicionales"
+ "Abrir la información de la aplicación"
+
+ %1$d más
+ %1$d más
+
+ "Esta aplicación está diseñada para una versión anterior de Android. Si se le deniega el permiso, puede dejar de funcionar de la forma prevista."
+ "realizar una acción desconocida"
+ "%1$d de %2$d aplicaciones permitidas"
+ "Mostrar aplicaciones del sistema"
+ "Ocultar aplicaciones del sistema"
+ "No hay aplicaciones"
+ "Ajustes de ubicación"
+ "%1$s es un proveedor de servicios de ubicación de este dispositivo. El acceso a la ubicación se puede modificar en los ajustes de ubicación."
+ "Si rechazas este permiso, es posible que funciones básicas de tu dispositivo dejen de funcionar correctamente."
+ "Aplicado por política"
+ "Acceso en segundo plano inhabilitado por política"
+ "Acceso en segundo plano habilitado por política"
+ "Acceso en primer plano habilitado por política"
+ "Controlado por el administrador"
+
+
+
+ "Siempre"
+ "Solo mientras se usa la app"
+ "Nunca"
+ "Cargando..."
+ "Todos los permisos"
+ "Otras funciones de la aplicación"
+ "Solicitud de permiso"
+ "Superposición de pantalla detectada"
+ "Para cambiar la configuración de este permiso, desactiva la superposición de pantalla en Ajustes > Aplicaciones"
+ "Abrir ajustes"
+ "Android Wear"
+ "Las acciones de instalar y desinstalar no pueden realizarse en Wear"
+ "Elige los permisos de acceso que quieres conceder a <b>%1$s</b>"
+ "<b>%1$s</b> se ha actualizado. Elige los permisos de acceso que quieres conceder a esta aplicación."
+ "Cancelar"
+ "Continuar"
+ "Permisos nuevos"
+ "Permisos actuales"
+ "Preparando aplicación…"
+ "Desconocido"
+ "Por motivos de seguridad, tu tablet no puede instalar aplicaciones desconocidas de esta fuente."
+ "Por motivos de seguridad, tu TV no puede instalar aplicaciones desconocidas de esta fuente."
+ "Por motivos de seguridad, tu teléfono no puede instalar aplicaciones desconocidas de esta fuente."
+ "Tu teléfono y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu teléfono o la pérdida de datos que se pueda derivar de su uso."
+ "Tu tablet y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu tablet o la pérdida de datos que se pueda derivar de su uso."
+ "Tu TV y tus datos personales son más vulnerables a los ataques de aplicaciones desconocidas. Al instalar esta aplicación, aceptas ser responsable de cualquier daño que sufra tu TV o la pérdida de datos que se pueda derivar de su uso."
+ "Continuar"
+ "Ajustes"
+ "Instalando/desinstalando apps para Wear"
+
diff --git a/packages/PackageInstaller/res/values-et-television/strings.xml b/packages/PackageInstaller/res/values-et-television/strings.xml
new file mode 100644
index 0000000000000..0abb2de0502fe
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Keela ja ära enam küsi"
+ "Saate seda hiljem muuta jaotises Seaded > Rakendused"
+ "%1$s/%2$s"
+ "Kuva süsteemirakendused"
+ "Rakenduste load"
+ "Rakenduste load"
+ "%1$s – load"
+ "Lisaload"
+ "%1$s – load"
+
diff --git a/packages/PackageInstaller/res/values-et-watch/strings.xml b/packages/PackageInstaller/res/values-et-watch/strings.xml
new file mode 100644
index 0000000000000..328e215001359
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Keela, ära enam küsi"
+ "%1$s/%2$s"
+ "Kuva süsteemirakendused"
+ "Ei saa muuta"
+ "Jah"
+ "Tühista"
+
diff --git a/packages/PackageInstaller/res/values-et/strings.xml b/packages/PackageInstaller/res/values-et/strings.xml
new file mode 100644
index 0000000000000..1754411da589d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-et/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paketiinstaller"
+ "Järgmine"
+ "Installi"
+ "Valmis"
+ "Tühista"
+ "Installimine ..."
+ "Paketi %1$s installimine …"
+ "Rakendus on installitud."
+ "Kas soovite rakenduse installida? See pääseb järgmiste üksuste juurde:"
+ "Kas soovite rakenduse installida? See ei nõua spetsiaalseid juurdepääsuõigusi."
+ "Kas soovite olemasoleva rakenduse värskenduse installida? Teie olemasolevad andmed jäävad alles. Värskendatud rakendus pääseb järgmiste funktsioonide juurde:"
+ "Kas soovite sisseehitatud rakenduse värskenduse installida? Teie olemasolevad andmed jäävad alles. Värskendatud rakendus pääseb järgmiste funktsioonide juurde:"
+ "Kas soovite installida olemasoleva rakenduse värskenduse? Olemasolevad andmed ei lähe kaduma. See ei nõua erijuurdepääsu."
+ "Kas soovite installida sisseehitatud rakenduse värskenduse? Olemasolevad andmed ei lähe kaduma. See ei nõua erijuurdepääsu."
+ "Rakendus pole installitud."
+ "Paketi installimine blokeeriti."
+ "Rakendust ei installitud, kuna pakett on olemasoleva paketiga vastuolus."
+ "Rakendust ei installitud, kuna rakendus ei ühildu teie tahvelarvutiga."
+ "Rakendus ei ühildu teie teleriga."
+ "Rakendust ei installitud, kuna rakendus ei ühildu teie telefoniga."
+ "Rakendust ei installitud, kuna pakett näib olevat sobimatu."
+ "Rakendust %1$s ei saa teie tahvelarvutisse installida."
+ "Rakendust %1$s ei saa teie telerisse installida."
+ "Rakendust %1$s ei saa teie telefoni installida."
+ "Ava"
+ "Administraator ei luba installida tundmatutest allikatest pärinevaid rakendusi"
+ "See kasutaja ei saa installida tundmatuid rakendusi"
+ "Kasutajal ei ole lubatud rakendusi installida"
+ "OK"
+ "Rakenduste haldamine"
+ "Pole ruumi"
+ "Rakendust %1$s ei saa installida. Vabastage mälu ja proovige uuesti."
+ "Rakendust ei leitud"
+ "Rakendust ei leitud installitud rakenduste loendist."
+ "Ei ole lubatud"
+ "Praegusel kasutajal ei ole lubatud seda desinstallimist teha."
+ "Viga"
+ "Rakendust ei saanud desinstallida."
+ "Rakenduse desinstallimine"
+ "Värskenduse desinstallimine"
+ "%1$s on osa järgmisest rakendusest:"
+ "Kas soovite selle rakenduse desinstallida?"
+ "Kas soovite desinstallida selle rakenduse ""kõikidelt"" kasutajatelt? Rakendus ja selle andmed eemaldatakse ""kõikidelt"" seadme kasutajatelt."
+ "Kas soovite kasutaja %1$s puhul rakenduse desinstallida?"
+ "Kas asendada see rakendus tehaseversiooniga? Kõik andmed eemaldatakse."
+ "Kas asendada see rakendus tehaseversiooniga? Kõik andmed eemaldatakse. See mõjutab kõiki seadme kasutajaid, sh neid, kellel on tööprofiilid."
+ "Käimasolevad desinstallimised"
+ "Ebaõnnestunud desinstallimised"
+ "Desinstallimine ..."
+ "Üksuse %1$s desinstallimine …"
+ "Desinstallimine on lõpetatud."
+ "Üksus %1$s on desinstallitud"
+ "Desinstallimine ebaõnnestus."
+ "Üksuse %1$s desinstallimine ebaõnnestus."
+ "Aktiivset seadme administraatori rakendust ei saa desinstallida"
+ "Kasutaja %1$s puhul ei saa aktiivset seadme administraatori rakendust desinstallida"
+ "Rakendus on mõne kasutaja ja profiili puhul vajalik, teiste puhul see desinstalliti"
+ "See rakendus on vajalik teie profiili jaoks ja seda ei saa desinstallida."
+ "Seadme administraator vajab seda rakendust ja seda ei saa desinstallida."
+ "Halda seadme administraatori rakendusi"
+ "Halda kasutajaid"
+ "Rakendust %1$s ei saanud desinstallida."
+ "Probleem paketi sõelumisel."
+ "Uus"
+ "Kõik"
+ "Privaatsus"
+ "Seadme juurdepääs"
+ "See värskendus ei nõua uusi lube."
+ "Keela"
+ "Lisateave"
+ "Keela ikkagi"
+ "%1$s/%2$s-st"
+ "Kas lubada rakendusel <b>%1$s</b> %2$s?"
+ "Kas lubada rakenduse puhul <b>%1$s</b> alati toiming %2$s?"
+ "Ainult rakenduse kasutamise ajal"
+ "Alati"
+ "Keela ja ära enam küsi"
+ "%1$d on keelatud"
+ "kõik on keelatud"
+ "mitte ükski pole keelatud"
+ "Luba"
+ "Rakendused"
+ "Rakenduste load"
+ "Ära enam küsi"
+ "Lube ei ole"
+ "Täiendavad load"
+ "Ava rakenduse teave"
+
+ Veel %1$d
+ Veel %1$d
+
+ "Rakendus on mõeldud Androidi vanemale versioonile. Kui keeldute loa andmisest, ei pruugi see ootuspäraselt töötada."
+ "tundmatu toiming"
+ "%1$d rakendust %2$d-st on lubatud"
+ "Kuva süsteem"
+ "Peida süsteem"
+ "Rakendusi pole"
+ "Asukohaseaded"
+ "Rakendus %1$s on selle seadme asukohateenuste pakkuja. Asukoha juurdepääsu saab muuta asukohaseadetes."
+ "Kui keelate loa, ei pruugi seadme põhifunktsioonid enam ootuspäraselt töötada."
+ "Eeskirjadega jõustatud"
+ "Reegli alusel on taustale juurdepääs keelatud"
+ "Reegli alusel on taustale juurdepääs lubatud"
+ "Reegli alusel on esiplaanile juurdepääs lubatud"
+ "Juhib administraator"
+
+
+
+ "Alati"
+ "Ainult rakenduse kasutamisel"
+ "Mitte kunagi"
+ "Laadimine ..."
+ "Kõik load"
+ "Rakenduse muud funktsioonid"
+ "Loa taotlus"
+ "Tuvastati ekraani ülekate"
+ "Selle loa seade muutmiseks peate esmalt välja lülitama ekraani ülekatte menüüs Seaded > Rakendused"
+ "Ava seaded"
+ "Android Wear"
+ "Wear ei toeta installimist/desinstallimist."
+ "Valige, millele lubate rakendusel <b>%1$s</b> juurde pääseda"
+ "Rakendust <b>%1$s</b> värskendati. Valige, millele lubate sellel rakendusel juurde pääseda."
+ "Tühista"
+ "Jätka"
+ "Uued load"
+ "Praegused load"
+ "Rakenduse ettevalmistamine …"
+ "Tundmatu"
+ "Teie turvalisuse huvides ei ole tahvelarvutil lubatud installida sellest allikast tundmatuid rakendusi."
+ "Teie turvalisuse huvides ei ole TV-l lubatud installida sellest allikast tundmatuid rakendusi."
+ "Teie turvalisuse huvides ei ole telefonil lubatud installida sellest allikast tundmatuid rakendusi."
+ "Teie telefon ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate telefoni kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."
+ "Teie tahvelarvuti ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate tahvelarvuti kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."
+ "Teie teler ja isiklikud andmed on tundmatute rakenduste rünnakute suhtes haavatavamad. Selle rakenduse installimisel nõustute, et vastutate teleri kahjude ja andmekao eest, mis võivad tuleneda selliste rakenduste kasutamisest."
+ "Jätka"
+ "Seaded"
+ "Weari rak. installimine/desinstallimine"
+
diff --git a/packages/PackageInstaller/res/values-eu-television/strings.xml b/packages/PackageInstaller/res/values-eu-television/strings.xml
new file mode 100644
index 0000000000000..78fffaeea1523
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Ukatu eta ez galdetu berriro"
+ "Hori geroago alda dezakezu Ezarpenak > Aplikazioak atalean"
+ "%1$s/%2$s"
+ "Erakutsi sistemaren aplikazioak"
+ "Aplikazio-baimenak"
+ "Aplikazio-baimenak"
+ "%1$s erabiltzeko baimenak"
+ "Baimen gehigarriak"
+ "%1$s erabiltzeko baimenak"
+
diff --git a/packages/PackageInstaller/res/values-eu-watch/strings.xml b/packages/PackageInstaller/res/values-eu-watch/strings.xml
new file mode 100644
index 0000000000000..0f08cada23acc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Ukatu; ez galdetu berriro"
+ "%1$s/%2$s"
+ "Erakutsi sistemaren aplikazioak"
+ "Ezin da aldatu"
+ "Bai"
+ "Utzi"
+
diff --git a/packages/PackageInstaller/res/values-eu/strings.xml b/packages/PackageInstaller/res/values-eu/strings.xml
new file mode 100644
index 0000000000000..0cb7e4c64cbf6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-eu/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pakete-instalatzailea"
+ "Hurrengoa"
+ "Instalatu"
+ "Eginda"
+ "Utzi"
+ "Instalatzen…"
+ "%1$s instalatzen…"
+ "Aplikazioa instalatu da."
+ "Aplikazioa instalatu nahi duzu? Elementu hauetarako sarbidea izango du:"
+ "Aplikazioa instalatu nahi duzu? Ez du sarbide berezirik behar."
+ "Aplikazioaren eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Eguneratutako aplikazioak elementu hauetarako sarbidea izango du:"
+ "Aplikazio integratu honen eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Eguneratutako aplikazioak elementu hauetarako sarbidea izango du:"
+ "Aplikazioaren eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Ez du sarbide berezirik behar."
+ "Aplikazio integratu honen eguneratzea instalatu nahi duzu? Lehendik dauden datuak ez dira galduko. Ez du sarbide berezirik behar."
+ "Ez da aplikazioa instalatu."
+ "Blokeatu egin da paketea instalatzeko aukera."
+ "Ez da instalatu aplikazioa, gatazka bat sortu delako lehendik dagoen pakete batekin."
+ "Ez da instalatu aplikazioa, ez delako tabletarekin bateragarria."
+ "Aplikazioa ez da telebistarekin bateragarria."
+ "Ez da instalatu aplikazioa, ez delako telefonoarekin bateragarria."
+ "Ez da instalatu aplikazioa, paketeak ez duelako balio."
+ "Ezin izan da %1$s tabletan instalatu."
+ "Ezin izan da instalatu %1$s telebistan."
+ "Ezin izan da %1$s telefonoan instalatu."
+ "Ireki"
+ "Administratzaileak ez du onartzen iturburu ezezagunetako aplikazioak instalatzea"
+ "Erabiltzaile honek ezin ditu instalatu aplikazio ezezagunak"
+ "Erabiltzaile honek ez du baimenik aplikazioak instalatzeko"
+ "Ados"
+ "Kudeatu aplikazioak"
+ "Ez dago behar adina toki"
+ "Ezin izan da %1$s instalatu. Egin toki pixka bat eta saiatu berriro."
+ "Ez da aplikazioa aurkitu"
+ "Aplikazioa ez da aurkitu instalatutako aplikazioen zerrendan."
+ "Ez dauka baimenik"
+ "Erabiltzaile honek ez dauka desinstalatzeko baimenik."
+ "Errorea"
+ "Ezin izan da desinstalatu aplikazioa."
+ "Desinstalatu aplikazioa"
+ "Desinstalatu eguneratzea"
+ "%1$s aplikazio honen zati da:"
+ "Aplikazioa desinstalatu nahi duzu?"
+ "Aplikazioa erabiltzaile ""guztientzat"" desinstalatu nahi duzu? Aplikazioa eta bere datu guztiak gailuko erabiltzaile ""guztiei"" ezabatuko zaizkie."
+ "%1$s erabiltzailearen aplikazioa desinstalatu nahi duzu?"
+ "Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira."
+ "Aplikazio hau jatorrizko bertsioarekin ordeztu nahi duzu? Datu guztiak ezabatuko dira. Gailuaren erabiltzaile guztiengan izango du eragina, laneko profilak dituztenengan barne."
+ "Abian diren desinstalatze-eragiketak"
+ "Huts egin duten desinstalatze-eragiketak"
+ "Desinstalatzen…"
+ "%1$s desinstalatzen…"
+ "Desinstalatu da."
+ "Desinstalatu da %1$s"
+ "Ezin izan da desinstalatu."
+ "Ezin izan da desinstalatu %1$s."
+ "Ezin da desinstalatu gailua administratzeko aplikazio aktiboa"
+ "Ezin da desinstalatu %1$s erabiltzailearen gailua administratzeko aplikazio aktiboa"
+ "Erabiltzaile edo profil batzuek behar dute aplikazio hau, baina desinstalatu egin da beste guztientzat."
+ "Zure profilak behar du aplikazio hau eta ezin da desinstalatu."
+ "Gailuaren administratzaileak aplikazio hori behar du eta ezin da desinstalatu."
+ "Kudeatu gailua administratzeko aplikazioak"
+ "Kudeatu erabiltzaileak"
+ "Ezin izan da %1$s desinstalatu."
+ "Arazo bat izan da paketea analizatzean."
+ "Berriak"
+ "Guztiak"
+ "Pribatutasuna"
+ "Gailurako sarbidea"
+ "Eguneratze honek ez du baimen berririk behar."
+ "Ukatu"
+ "Informazio gehiago"
+ "Ukatu hala ere"
+ "%1$s/%2$s"
+ "<b>%1$s</b> aplikazioari \"%2$s\" izeneko baimena eman nahi diozu?"
+ "Beti eman nahi diozu \"%2$s\" baimena <b>%1$s</b> aplikazioari?"
+ "Aplikazioa erabiltzean soilik"
+ "Beti"
+ "Baztertu eta ez galdetu berriro"
+ "%1$d desgaituta"
+ "guztiak desgaituta"
+ "guztiak gaituta"
+ "Baimendu"
+ "Aplikazioak"
+ "Aplikazio-baimenak"
+ "Ez galdetu berriro"
+ "Ez dago baimenik"
+ "Baimen gehigarriak"
+ "Ireki aplikazioaren informazioa"
+
+ %1$d gehiago
+ %1$d gehiago
+
+ "Aplikazio hau Android-en bertsio zaharrago baterako diseinatuta dago. Baimena ukatzen baduzu, agian aurrerantzean ez du behar bezain ondo funtzionatuko."
+ "Gauzatu ekintza ezezagunak"
+ "%1$d/%2$d aplikaziok dute baimena"
+ "Erakutsi sistema"
+ "Ezkutatu sistema"
+ "Ez dago aplikaziorik"
+ "Kokapen-ezarpenak"
+ "%1$s gailu honen kokapen-zerbitzuen hornitzailea da. Kokapenerako sarbidea kokapen-ezarpenetatik alda daiteke."
+ "Baimena ematen ez baduzu, baliteke gailuaren oinarrizko eginbide batzuek behar bezala ez funtzionatzea."
+ "Gidalerroen bidez aplikatzen da"
+ "Gidalerro batek eskatuta, atzeko planoa atzitzeko aukera desgaitu da"
+ "Gidalerro batek eskatuta, atzeko planoa atzitzeko aukera gaitu da"
+ "Gidalerro batek eskatuta, aurreko planoa atzitzeko aukera gaitu da"
+ "Administratzaileak kontrolatzen du"
+
+
+
+ "Beti"
+ "Aplikazioa erabiltzean soilik"
+ "Inoiz ez"
+ "Kargatzen…"
+ "Baimen guztiak"
+ "Aplikazioaren beste gaitasun batzuk"
+ "Baimen-eskaera"
+ "Pantailaren gainjartzea detektatu da"
+ "Baimen-ezarpen hau aldatzeko, pantailaren gainjartzea desaktibatu behar duzu Ezarpenak > Aplikazioak atalean"
+ "Ireki ezarpenak"
+ "Android Wear"
+ "Instalatzeko eta desinstalatzeko ekintzak ezin dira gauzatu Wear gailuetan."
+ "Aukeratu zer atzi dezakeen <b>%1$s</b> aplikazioak"
+ "Eguneratu egin da <b>%1$s</b>. Aukeratu aplikazioak zer atzi dezakeen."
+ "Utzi"
+ "Jarraitu"
+ "Baimen berriak"
+ "Uneko baimenak"
+ "Aplikazioa prestatzen…"
+ "Ezezaguna"
+ "Segurtasuna bermatzeko, tableta honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."
+ "Segurtasuna bermatzeko, telebista honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."
+ "Segurtasuna bermatzeko, telefono honetan ezin dira instalatu iturburu honetako aplikazio ezezagunak."
+ "Telefonoak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartzen duzu hura erabiltzeagatik telefonoak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."
+ "Tabletak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Aplikazio hau instalatzen baduzu, onartzen duzu hura erabiltzeagatik tabletak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."
+ "Telebistak eta datu pertsonalek aplikazio ezezagunen erasoak jaso ditzakete. Iturburu honetako aplikazioak instalatzen badituzu, onartzen duzu haiek erabiltzeagatik telebistak jasan ditzakeen kalteen edo datu-galeren erantzulea zeu zarela."
+ "Egin aurrera"
+ "Ezarpenak"
+ "Wear aplikazioak instalatzea/desinstalatzea"
+
diff --git a/packages/PackageInstaller/res/values-fa-television/strings.xml b/packages/PackageInstaller/res/values-fa-television/strings.xml
new file mode 100644
index 0000000000000..95f2a54cce006
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "اجازه ندارد و دیگر سؤال نشود"
+ "میتوانید بعداً آن را در تنظیمات > برنامهها تغییر دهید"
+ "%1$s / %2$s"
+ "نمایش برنامههای سیستم"
+ "مجوزهای برنامه"
+ "مجوزهای برنامه"
+ "مجوزهای %1$s"
+ "مجوزهای بیشتر"
+ "مجوزهای %1$s"
+
diff --git a/packages/PackageInstaller/res/values-fa-watch/strings.xml b/packages/PackageInstaller/res/values-fa-watch/strings.xml
new file mode 100644
index 0000000000000..8d149541b8599
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "رد شود، دیگر سؤال نشود"
+ "%1$s / %2$s"
+ "نمایش برنامههای سیستم"
+ "نمیتواند تغییر کند"
+ "بله"
+ "لغو"
+
diff --git a/packages/PackageInstaller/res/values-fa/strings.xml b/packages/PackageInstaller/res/values-fa/strings.xml
new file mode 100644
index 0000000000000..22f1a257a7e4c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fa/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "نصبکننده بسته"
+ "بعدی"
+ "نصب"
+ "تمام"
+ "لغو"
+ "در حال نصب…"
+ "درحال نصب %1$s…"
+ "برنامه نصب شد."
+ "آیا میخواهید این برنامه را نصب کنید؟ این برنامه به این موارد دسترسی خواهد یافت:"
+ "آیا میخواهید این برنامه را نصب کنید؟ این برنامه به دسترسی خاصی نیاز ندارد."
+ "آیا میٰخواهید بهٰروزرسانی این برنامه کنونی را نصب کنید؟ داده کنونی شما از بین نمیرود. برنامه بهروزرسانی شده دسترسی خواهد داشت به:"
+ "آیا میخواهید بهروزرسانی این برنامه جاسازی شده را نصب کنید؟ دادههای کنونی شما از بین نمیرود. برنامه بهروزرسانی شده دسترسی خواهد داشت به:"
+ "آیا میخواهید یک بهروزرسانی برای این برنامه کاربردی موجود نصب کنید؟ دادههای موجود شما از دست نخواهد رفت. به دسترسی ویژهای نیاز ندارد."
+ "آیا میخواهید یک بهروزرسانی برای این برنامه کاربردی داخلی نصب کنید؟ دادههای موجود شما از دست نخواهد رفت. به دسترسی ویژهای نیاز ندارد."
+ "برنامه نصب نشد."
+ "از نصب شدن بسته جلوگیری شد."
+ "برنامه نصب نشد چون بسته با بسته موجود تداخل دارد."
+ "برنامه نصب نشد چون با رایانه لوحیتان سازگار نیست."
+ "این برنامه با تلویزیون شما سازگار نیست."
+ "برنامه نصب نشد چون با تلفنتان سازگار نیست."
+ "برنامه نصب نشد چون به نظر میرسد بسته معتبر نیست."
+ "%1$s در رایانهٔ لوحی شما نصب نشد."
+ "%1$s را نمیتوان روی تلویزیون شما نصب کرد."
+ "%1$s در تلفن شما نصب نشد."
+ "باز کردن"
+ "سرپرست سیستم شما اجازه نمیدهد برنامههای دریافتشده از منابع ناشناس را نصب کنید"
+ "این کاربر نمیتواند برنامههای ناشناس نصب کند"
+ "این کاربر مجاز به نصب برنامه نیست"
+ "تأیید"
+ "مدیریت برنامهها"
+ "فضا کافی نیست"
+ "%1$s نصب نمیشود. مقداری از فضا را آزاد کرده و دوباره امتحان کنید."
+ "برنامه یافت نشد"
+ "برنامه در فهرست برنامههای نصب شده یافت نشد."
+ "مجاز نیست"
+ "کاربر کنونی مجاز به انجام این حذف نصب نیست."
+ "خطا"
+ "برنامه را نمیتوان حذف نصب کرد."
+ "حذف نصب برنامه"
+ "حذف نصب بهروزرسانی"
+ "%1$s قسمتی از برنامه زیر است:"
+ "میخواهید این برنامه را حذف نصب کنید؟"
+ "آیا میخواهید این برنامه را برای ""همه"" کاربران حذف کنید؟ این برنامه کاربردی و دادههای آن برای ""همه"" کاربران این دستگاه حذف خواهد شد."
+ "آیا میخواهید این برنامه را برای این کاربر %1$s حذف نصب کنید؟"
+ "این برنامه با نسخه کارخانه جایگزین شود؟ همه دادهها پاک میشوند."
+ "این برنامه با نسخه کارخانه جایگزین شود؟ همه دادهها پاک میشوند. این کار همه کاربران این دستگاه (از جمله کاربرانی که نمایه کاری دارند) را تحت تأثیر قرار خواهد داد."
+ "حذفنصبهای درحال انجام"
+ "حذفنصبهای ناموفق"
+ "در حال حذف نصب..."
+ "درحال حذف نصب %1$s…"
+ "حذف نصب پایان یافت."
+ "%1$s را حذف نصب کرد"
+ "حذف نصب انجام نشد."
+ "%1$s باموفقیت حذف نصب شد."
+ "نمیتوان برنامه فعال سرپرست دستگاه را حذف نصب کرد"
+ "نمیتوان برنامه فعال سرپرست دستگاه را برای %1$s حذف نصب کرد"
+ "این برنامه برای برخی کاربران یا نمایهها ضروری است و برای بقیه حذف نصب شد"
+ "این برنامه برای نمایه شما لازم است و نمیتوان آن را حذف نصب کرد."
+ "سرپرست دستگاه شما این برنامه را لازم کرده است و نمیتواند حذف نصب شود."
+ "مدیریت برنامههای سرپرست دستگاه"
+ "مدیریت کاربران"
+ "%1$s حذف نصب نشد."
+ "مشکلی در تجزیه این بسته وجود داشت."
+ "جدید"
+ "همه موارد"
+ "حریم خصوصی"
+ "دسترسی به دستگاه"
+ "این بهروزرسانی به مجوز جدیدی نیاز ندارد."
+ "اجازه ندارد"
+ "اطلاعات بیشتر"
+ "در هر صورت نادیده گرفته شود"
+ "%1$s از %2$s"
+ "به<b>%1$s</b> اجازه %2$s را میدهید؟"
+ "همیشه به <b>%1$s</b> برای %2$s اجازه داده شود؟"
+ "فقط هنگام استفاده از برنامه"
+ "همیشه"
+ "رد شود و دیگر سؤال نشود"
+ "%1$d مجوز غیرفعال هستند"
+ "همه مجوزها غیرفعال هستند"
+ "هیچ موردی غیرفعال نیست"
+ "مجاز است"
+ "برنامهها"
+ "مجوزهای برنامه"
+ "دوباره سؤال نشود"
+ "مجوزی موجود نیست"
+ "مجوزهای بیشتر"
+ "باز کردن اطلاعات برنامه"
+
+ %1$d مورد دیگر
+ %1$d مورد دیگر
+
+ "این برنامه برای یک نسخه قدیمیتر از Android طراحی شده بود. نپذیرفتن اجازه ممکن است باعث شود که برنامه دیگر به صورتی که موردنظر است کار نکند."
+ "انجام یک اقدام ناشناس"
+ "%1$d برنامه از %2$d برنامه مجاز است"
+ "نمایش سیستم"
+ "پنهان کردن سیستم"
+ "برنامهای موجود نیست"
+ "تنظیمات مکان"
+ "%1$s یکی از ارائهدهندگان سرویسهای مکان برای این دستگاه است. با رفتن به تنظیمات مکان میتوانید دسترسی به موقعیت مکانی را تغییر دهید."
+ "اگر این اجازه را رد کنید، ممکن است قابلیتهای اصلی دستگاهتان دیگر عملکرد موردانتظار را نداشته باشند."
+ "اجرا توسط خطمشی"
+ "دسترسی به پسزمینه بهموجب خطمشی غیرفعال شد"
+ "دسترسی به پسزمینه بهموجب خطمشی فعال شد"
+ "دسترسی به پیشزمینه بهموجب خطمشی فعال شد"
+ "توسط سرپرست سیستم کنترل میشود"
+
+
+
+ "همیشه"
+ "فقط هنگام استفاده از برنامه"
+ "هرگز"
+ "درحال بارگیری…"
+ "همه مجوزها"
+ "سایر قابلیتهای برنامه"
+ "درخواست مجوز"
+ "همپوشانی صفحه شناسایی شد"
+ "برای تغییر این تنظیم مجوز، ابتدا باید همپوشانی صفحه را از «تنظیمات > برنامهها» خاموش کنید"
+ "باز کردن تنظیمات"
+ "Android Wear"
+ "کنشهای نصب/حذف نصب در Wear پشتیبانی نمیشود."
+ "انتخاب کنید <b>%1$s</b> اجازه دارد به چه چیزی دسترسی پیدا کند"
+ "<b>%1$s</b> بهروزرسانی شده است. انتخاب کنید این برنامه اجازه دارد به چه چیزی دسترسی پیدا کند."
+ "لغو"
+ "ادامه"
+ "مجوزهای جدید"
+ "مجوزهای کنونی"
+ "مرحلهبندی برنامه…"
+ "نامشخص"
+ "برای امنیت شما، رایانه لوحیتان اجازه نمیدهد از این منبع برنامههای ناشناس نصب شوند."
+ "برای امنیت شما، تلویزیونتان اجازه نمیدهد از این منبع برنامههای ناشناس نصب شوند."
+ "برای امنیت شما، تلفنتان اجازه نمیدهد از این منبع برنامههای ناشناس نصب شوند."
+ "تلفن و دادههای شخصیتان در برابر حمله برنامههای ناشناس آسیبپذیرتر هستند. با نصب این برنامه، موافقت میکنید که مسئول هرگونه آسیب به تلفن یا از دست رفتن دادهای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."
+ "رایانه لوحی و دادههای شخصیتان در برابر حمله برنامههای ناشناس آسیبپذیرتر هستند. با نصب این برنامه، موافقت میکنید که مسئول هرگونه آسیب به رایانه لوحی یا از دست رفتن دادهای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."
+ "تلویزیون و دادههای شخصیتان در برابر حمله برنامههای ناشناس آسیبپذیرتر هستند. با نصب این برنامه، موافقت میکنید که مسئول هرگونه آسیب به تلویزیون یا از دست رفتن دادهای هستید که ممکن است در نتیجه استفاده از آن به وجود آید."
+ "ادامه"
+ "تنظیمات"
+ "نصب/حذف نصب برنامههای پوشیدنی"
+
diff --git a/packages/PackageInstaller/res/values-fi-television/strings.xml b/packages/PackageInstaller/res/values-fi-television/strings.xml
new file mode 100644
index 0000000000000..fbcbe4f544f63
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Hylkää äläkä kysy uudelleen"
+ "Voit muuttaa tätä myöhemmin valitsemalla Asetukset > Sovellukset."
+ "%1$s/%2$s"
+ "Näytä järjestelmäsovellukset"
+ "Sovellusten käyttöoikeudet"
+ "Sovellusten käyttöoikeudet"
+ "Käyttöoikeudet – %1$s"
+ "Lisäkäyttöoikeudet"
+ "Käyttöoikeudet – %1$s"
+
diff --git a/packages/PackageInstaller/res/values-fi-watch/strings.xml b/packages/PackageInstaller/res/values-fi-watch/strings.xml
new file mode 100644
index 0000000000000..292c4173a1233
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Hylkää, äläkä kysy uudelleen"
+ "%1$s/%2$s"
+ "Näytä järjestelmäsovellukset"
+ "Ei muutettavissa"
+ "Kyllä"
+ "Peruuta"
+
diff --git a/packages/PackageInstaller/res/values-fi/strings.xml b/packages/PackageInstaller/res/values-fi/strings.xml
new file mode 100644
index 0000000000000..7ce75d17f3eab
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fi/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paketin asentaja"
+ "Seuraava"
+ "Asenna"
+ "Valmis"
+ "Peruuta"
+ "Asennetaan…"
+ "Asennetaan kohdetta %1$s…"
+ "Sovellus on asennettu."
+ "Haluatko asentaa tämän sovelluksen? Se saa käyttöönsä seuraavat ominaisuudet:"
+ "Haluatko asentaa tämän sovelluksen? Se ei vaadi erityisiä käyttöoikeuksia."
+ "Haluatko asentaa päivityksen tähän olemassa olevaan sovellukseen? Olemassa olevat tiedot eivät katoa. Päivitetty sovellus saa käyttöönsä seuraavat ominaisuudet:"
+ "Haluatko asentaa päivityksen tähän sisäiseen sovellukseen? Olemassa olevat tiedot eivät katoa. Päivitetty sovellus saa käyttöönsä seuraavat ominaisuudet:"
+ "Haluatko asentaa päivityksen tähän sovellukseen? Et menetä nykyisiä tietojasi. Päivitys ei edellytä erityisiä käyttöoikeuksia."
+ "Haluatko asentaa päivityksen tähän laitteen mukana tulleeseen sovellukseen? Et menetä nykyisiä tietojasi. Päivitys ei edellytä erityisiä käyttöoikeuksia."
+ "Sovellusta ei asennettu."
+ "Paketin asennus estettiin."
+ "Sovellusta ei asennettu, koska paketti on ristiriidassa nykyisen paketin kanssa."
+ "Sovellusta ei asennettu, koska se ei ole yhteensopiva tabletin kanssa."
+ "Tämä sovellus ei ole yhteensopiva televisiosi kanssa."
+ "Sovellusta ei asennettu, koska se ei ole yhteensopiva puhelimen kanssa."
+ "Sovellusta ei asennettu, koska paketti vaikuttaa virheelliseltä."
+ "Sovelluksen %1$s asentaminen tähän tablet-laitteeseen epäonnistui."
+ "%1$s ei asennu televisioosi."
+ "Sovelluksen %1$s asentaminen puhelimeesi ei onnistunut."
+ "Avaa"
+ "Järjestelmänvalvoja ei salli sovellusten asentamista tuntemattomista lähteistä."
+ "Tämä käyttäjä ei voi asentaa tuntemattomia sovelluksia."
+ "Tämä käyttäjä ei voi asentaa sovelluksia."
+ "OK"
+ "Hallinnoi sovelluksia"
+ "Tallennustila loppu"
+ "Sovelluksen %1$s asentaminen epäonnistui. Vapauta tallennustilaa ja yritä uudelleen."
+ "Sovellusta ei löydy"
+ "Sovellusta ei löydy asennettujen sovelluksien luettelosta."
+ "Ei sallittu"
+ "Nykyisellä käyttäjällä ei ole oikeutta suorittaa tätä poistoa."
+ "Virhe"
+ "Sovelluksen poistaminen epäonnistui."
+ "Poista sovellus"
+ "Poista päivitys"
+ "%1$s on osa seuraavaa sovellusta:"
+ "Haluatko poistaa tämän sovelluksen?"
+ "Haluatko poistaa tämän sovelluksen ""kaikilta"" käyttäjiltä? Sovellus ja sen tiedot poistetaan ""kaikilta"" laitteen käyttäjiltä."
+ "Haluatko poistaa tämän sovelluksen käyttäjältä %1$s?"
+ "Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki tiedot poistetaan."
+ "Haluatko korvata tämän sovelluksen tehdasversiolla? Kaikki tiedot poistetaan. Tämä vaikuttaa kaikkiin laitteen käyttäjiin, myös työprofiileihin."
+ "Käynnissä olevat poistot"
+ "Epäonnistuneet poistot"
+ "Poistetaan..."
+ "Poistetaan pakettia %1$s…"
+ "Poisto valmis."
+ "%1$s poistettu"
+ "Poisto epäonnistui."
+ "%1$s on poistettu."
+ "Aktiivista laitteenhallintasovellusta ei voi poistaa käytöstä."
+ "Käyttäjän %1$s aktiivista laitteenhallintasovellusta ei voi poistaa käytöstä."
+ "Jotkin käyttäjät/profiilit tarvitsevat tätä sovellusta ja se poistettiin muista."
+ "Profiilisi käyttö edellyttää tätä sovellusta. Sovellusta ei voi poistaa."
+ "Laitteen järjestelmänvalvoja tarvitsee tätä sovellusta eikä sitä voi poistaa."
+ "Hallinnoi laitteenhallintasovelluksia"
+ "Hallinnoi käyttäjiä"
+ "Sovelluksen %1$s poistaminen epäonnistui"
+ "Paketin jäsentämisessä esiintyi ongelma."
+ "Uusi"
+ "Kaikki"
+ "Tietosuoja"
+ "Laitteen käyttö"
+ "Tämä päivitys ei vaadi uusia käyttöoikeuksia."
+ "Estä"
+ "Lisätietoja"
+ "Kiellä silti"
+ "%1$s/%2$s"
+ "Saako <b>%1$s</b> %2$s?"
+ "Saako <b>%1$s</b> aina %2$s?"
+ "Vain sovelluksen käytön aikana"
+ "Aina"
+ "Hylkää äläkä kysy uudelleen"
+ "%1$d pois käytöstä"
+ "kaikki pois käytöstä"
+ "kaikki käytössä"
+ "Salli"
+ "Sovellukset"
+ "Sovellusten käyttöoikeudet"
+ "Älä kysy uudestaan"
+ "Ei käyttöoikeuksia"
+ "Lisäkäyttöoikeudet"
+ "Avaa sovelluksen tiedot"
+
+ %1$d lisää
+ %1$d lisää
+
+ "Tämä sovellus on suunniteltu vanhemmalle Android-versiolle. Se ei välttämättä toimi oikein, jos käyttöoikeuksia ei sallita."
+ "suorita tuntematon toiminto"
+ "Sallitut sovellukset: %1$d/%2$d"
+ "Näytä järjestelmä"
+ "Piilota järjestelmä"
+ "Ei sovelluksia"
+ "Sijaintiasetukset"
+ "%1$s on tämän laitteen sijaintipalveluiden tarjoaja. Sijainnin käyttöoikeutta voi muokata sijaintiasetuksissa."
+ "Jos peruutat tämän käyttöoikeuden, laitteesi perustoiminnot eivät välttämättä enää toimi oikein."
+ "Käytännön vahvistama"
+ "Käytäntö estää taustakäytön"
+ "Käytäntö sallii taustakäytön"
+ "Käytäntö sallii käytön etualalla"
+ "Järjestelmänvalvoja hallinnoi tätä"
+
+
+
+ "Aina"
+ "Vain sovelluksen käytön aikana"
+ "Ei koskaan"
+ "Ladataan…"
+ "Kaikki käyttöoikeudet"
+ "Muut sovellusluvat"
+ "Lupapyyntö"
+ "Näytön peittokuva havaittiin"
+ "Ennen kuin voit muokata tätä käyttöoikeusasetusta, sinun täytyy poistaa näytön peittokuva käytöstä Asetukset-valikon Sovellukset-kohdasta."
+ "Avaa Asetukset"
+ "Android Wear"
+ "Wear ei tue asennus- ja poistotoimintoja."
+ "Valitse, mitä käyttöoikeuksia sovellukselle <b>%1$s</b> myönnetään."
+ "<b>%1$s</b> on päivitetty. Valitse, mitä käyttöoikeuksia tälle sovellukselle myönnetään."
+ "Peruuta"
+ "Jatka"
+ "Uudet käyttöoikeudet"
+ "Nykyiset käyttöoikeudet"
+ "Valmistellaan sovellusta…"
+ "Tuntematon"
+ "Turvallisuussyistä tabletti ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."
+ "Turvallisuussyistä televisiosi ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."
+ "Turvallisuussyistä puhelin ei voi asentaa tuntemattomia sovelluksia tästä lähteestä."
+ "Tuntemattomat sovellukset voivat helpommin kaapata puhelimesi ja henkilötietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa puhelimellesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."
+ "Tuntemattomat sovellukset voivat helpommin kaapata tablettisi ja henkilötietosi. Lataamalla sovelluksia tästä lähteestä hyväksyt, että olet itse vastuussa tabletillesi aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."
+ "Tuntemattomat sovellukset voivat helpommin kaapata televisiosi ja henkilötietosi. Lataamalla sovelluksen hyväksyt, että olet itse vastuussa mahdollisista televisiolle aiheutuvista vahingoista tai tietojen menetyksestä, jotka voivat johtua sovellusten käytöstä."
+ "Jatka"
+ "Asetukset"
+ "Wear-sovellusten asennus/poistaminen"
+
diff --git a/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml
new file mode 100644
index 0000000000000..f9b360e45de13
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Refuser et ne plus demander"
+ "Vous pourrez modifier ce choix plus tard dans le menu Paramètres > Applications"
+ "%1$s/%2$s"
+ "Afficher les applications système"
+ "Autorisations de l\'application"
+ "Autorisations de l\'application"
+ "Autorisations pour %1$s"
+ "Autorisations supplémentaires"
+ "Autorisations pour %1$s"
+
diff --git a/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml b/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml
new file mode 100644
index 0000000000000..ad86d01661403
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Refuser et ne plus demander"
+ "%1$s / %2$s"
+ "Afficher les applications système"
+ "Inchangeable"
+ "Oui"
+ "Annuler"
+
diff --git a/packages/PackageInstaller/res/values-fr-rCA/strings.xml b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
new file mode 100644
index 0000000000000..e44e465aa4762
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-rCA/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Programme installation trousse"
+ "Suivante"
+ "Installer"
+ "Terminé"
+ "Annuler"
+ "Installation..."
+ "Installation de %1$s en cours…"
+ "Application installée."
+ "Voulez-vous installer cette application? Elle pourra :"
+ "Voulez-vous installer cette application? Elle n\'exige aucun accès particulier."
+ "Voulez-vous installer une mise à jour pour cette application? Vos données existantes seront conservées. L\'application mise à jour aura accès à :"
+ "Voulez-vous installer une mise à jour pour cette application intégrée? Vos données existantes seront conservées. L\'application mise à jour aura accès à :"
+ "Voulez-vous installer une mise à jour pour cette application? Vos données ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."
+ "Voulez-vous installer une mise à jour pour cette application intégrée? Vos données existantes ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."
+ "Application non installée."
+ "L\'installation du paquet a été bloquée."
+ "L\'application n\'a pas été installée, car le paquet entre en conflit avec un paquet existant."
+ "L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre tablette."
+ "Cette application n\'est pas compatible avec votre téléviseur."
+ "L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre téléphone."
+ "L\'application n\'a pas été installée, car elle ne semble pas être valide."
+ "Impossible d\'installer %1$s sur cette tablette."
+ "%1$s n\'a pas pu être installée sur votre téléviseur."
+ "Impossible d\'installer %1$s sur ce téléphone."
+ "Ouvrir"
+ "Votre administrateur n\'autorise pas l\'installation d\'applications obtenues à partir de sources inconnues"
+ "Cet utilisateur ne peut pas installer les applications inconnues"
+ "Cet utilisateur n\'est pas autorisé à installer des applications"
+ "OK"
+ "Gérer les applications"
+ "Espace insuffisant"
+ "Impossible d\'installer %1$s. Veuillez libérer de l\'espace, puis réessayer."
+ "Application non trouvée"
+ "L\'application ne figure pas dans la liste des applications installées."
+ "Non autorisé"
+ "L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."
+ "Erreur"
+ "L\'application n\'a pas pu être désinstallée."
+ "Désinstaller l\'application"
+ "Désinstaller mise à jour"
+ "%1$s fait partie de l\'application suivante :"
+ "Voulez-vous désinstaller cette application?"
+ "Voulez-vous désinstaller cette application pour ""tous"" les utilisateurs? L\'application et ses données seront supprimées pour ""tous"" les utilisateurs de l\'appareil."
+ "Voulez-vous désinstaller cette application pour l\'utilisateur %1$s?"
+ "Remplacer cette application par la version d\'usine? Toutes les données seront supprimées."
+ "Remplacer cette application par la version d\'usine? Toutes les données seront supprimées. Cela touchera tous les utilisateurs de cet appareil, y compris ceux qui utilisent un profil professionnel."
+ "Désinstallations en cours"
+ "Désinstallations échouées"
+ "Désinstallation..."
+ "Désinstallation de %1$s en cours…"
+ "Désinstallation terminée."
+ "L\'application %1$s a bien été désinstallée"
+ "Échec de la désinstallation."
+ "La désinstallation de %1$s n\'a pas réussi."
+ "Impossible de désinstaller une application d\'administration de l\'appareil active"
+ "Impossible de désinstaller une application d\'administration de l\'appareil active pour %1$s"
+ "Cette application est nécessaire pour certains utilisateurs ou profils, et elle a été désinstallée pour d\'autres."
+ "Cette application est nécessaire pour votre profil et ne peut pas être désinstallée."
+ "Impossible de désinstaller l\'application : requise par administrateur appareil."
+ "Gérer les applications d\'administration d\'appareils"
+ "Gérer les utilisateurs"
+ "Impossible de désinstaller %1$s."
+ "Un problème est survenu lors de l\'analyse du paquet."
+ "Nouvelles"
+ "Toutes"
+ "Confidentialité"
+ "Accès à l\'appareil"
+ "Cette mise à jour n\'exige pas de nouvelles autorisations."
+ "Refuser"
+ "En savoir plus"
+ "Refuser quand même"
+ "%1$s de %2$s"
+ "Autoriser <b>%1$s</b> à %2$s?"
+ "Toujours autoriser « %1$s » à %2$s?"
+ "Seulement durant l\'utilisation de l\'application"
+ "Toujours"
+ "Refuser et ne plus demander"
+ "%1$d autorisation(s) désactivée(s)"
+ "toutes les autorisations sont désactivées"
+ "aucune autorisation n\'est désactivée"
+ "Autoriser"
+ "Applications"
+ "Autorisations applis"
+ "Ne plus demander"
+ "Aucune autorisation"
+ "Autorisations supplémentaires"
+ "Ouvrir l\'information sur l\'application"
+
+ %1$d autre
+ %1$d autres
+
+ "Cette application a été conçue pour une version antérieure d\'Android. Si vous n\'accordez pas l\'autorisation, il se peut qu\'elle ne fonctionne plus correctement."
+ "effectuer une action inconnue"
+ "%1$d application(s) autorisée(s) sur %2$d"
+ "Afficher le système"
+ "Masquer le système"
+ "Aucune application"
+ "Paramètres de localisation"
+ "%1$s est un fournisseur de services de localisation pour cet appareil. L\'accès à la position peut être modifié dans le menu des paramètres de localisation."
+ "Si vous refusez cette autorisation, il est possible que cela touche certaines fonctionnalités de base de votre appareil."
+ "Activé conformément à la politique"
+ "L\'accès en arrière-plan est désactivé par la politique"
+ "L\'accès en arrière-plan est activé par la politique"
+ "L\'accès en avant-plan est activé par la politique"
+ "Contrôlé par l\'administrateur"
+
+
+
+ "Toujours"
+ "Seulement durant l\'util. de l\'appli"
+ "Jamais"
+ "Chargement en cours…"
+ "Toutes les autorisations"
+ "Autres autorisations de l\'application"
+ "Demande d\'autorisation"
+ "La superposition d\'écran a été détectée"
+ "Pour modifier ce paramètre d\'autorisation, vous devez tout d\'abord désactiver la superposition d\'écran en accédant à Paramètres > Applications."
+ "Ouvrir les paramètres"
+ "Android Wear"
+ "Les actions d\'installation et de désinstallation ne sont pas prises en charge par Android Wear."
+ "Définissez les autorisations d\'accès de l\'application « %1$s »"
+ "L\'application « %1$s » a été mise à jour. Définissez ses autorisations d\'accès."
+ "Annuler"
+ "Continuer"
+ "Nouvelles autorisations"
+ "Autorisations actuelles"
+ "Pré-production de l\'application en cours…"
+ "Inconnue"
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur cette tablette."
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur."
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléphone."
+ "Votre téléphone et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de l\'utilisation de telles applications."
+ "Votre tablette et vos données personnelles sont plus vulnérables aux attaques provenant d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de l\'utilisation de telles applications."
+ "Votre téléviseur et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."
+ "Continuer"
+ "Paramètres"
+ "Installer/désinstaller applis Google Wear"
+
diff --git a/packages/PackageInstaller/res/values-fr-television/strings.xml b/packages/PackageInstaller/res/values-fr-television/strings.xml
new file mode 100644
index 0000000000000..fa16b94077030
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Refuser et ne plus demander"
+ "Vous pourrez modifier ce paramètre plus tard sous Paramètres > Applications."
+ "%1$s/%2$s"
+ "Afficher les applications système"
+ "Autorisations de l\'application"
+ "Autorisations de l\'application"
+ "Autorisations pour %1$s"
+ "Autorisations supplémentaires"
+ "Autorisations pour %1$s"
+
diff --git a/packages/PackageInstaller/res/values-fr-watch/strings.xml b/packages/PackageInstaller/res/values-fr-watch/strings.xml
new file mode 100644
index 0000000000000..a172e1c7f191a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Refuser et ne plus demander"
+ "%1$s/%2$s"
+ "Afficher les applications système"
+ "Non modifiable"
+ "Oui"
+ "Annuler"
+
diff --git a/packages/PackageInstaller/res/values-fr/strings.xml b/packages/PackageInstaller/res/values-fr/strings.xml
new file mode 100644
index 0000000000000..58d9123a70894
--- /dev/null
+++ b/packages/PackageInstaller/res/values-fr/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Programme d\'installation du kit"
+ "Suivant"
+ "Installer"
+ "OK"
+ "Annuler"
+ "Installation..."
+ "Installation de %1$s…"
+ "Application installée."
+ "Voulez-vous installer cette application ? Elle permet les actions suivantes :"
+ "Voulez-vous installer cette application ? Elle n\'exige aucun accès particulier."
+ "Voulez-vous installer une mise à jour pour cette application ? Vos données existantes seront conservées. L\'application mise à jour pourra :"
+ "Voulez-vous installer une mise à jour pour cette application intégrée ? Vos données existantes seront conservées. L\'application mise à jour pourra :"
+ "Voulez-vous installer une mise à jour pour cette application ? Vos données ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."
+ "Voulez-vous installer une mise à jour pour cette application intégrée ? Vos données existantes ne seront pas perdues. Aucun droit d\'accès spécial n\'est requis."
+ "Application non installée."
+ "L\'installation du package a été bloquée."
+ "L\'application n\'a pas été installée, car le package entre en conflit avec un package existant."
+ "L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre tablette."
+ "Cette application n\'est pas compatible avec votre téléviseur."
+ "L\'application n\'a pas été installée, car elle n\'est pas compatible avec votre téléphone."
+ "L\'application n\'a pas été installée, car le package semble ne pas être valide."
+ "Impossible d\'installer %1$s sur cette tablette."
+ "Impossible d\'installer %1$s sur votre téléviseur."
+ "Impossible d\'installer %1$s sur ce téléphone."
+ "Ouvrir"
+ "Votre administrateur n\'autorise pas l\'installation d\'applications obtenues à partir de sources inconnues"
+ "Cet utilisateur ne peut pas installer d\'applications inconnues"
+ "Cet utilisateur n\'est pas autorisé à installer des applications"
+ "OK"
+ "Gérer les applications"
+ "Mémoire insuffisante"
+ "Impossible d\'installer %1$s. Veuillez libérer de l\'espace, puis réessayer."
+ "Application non trouvée"
+ "L\'application ne figure pas dans la liste des applications installées."
+ "Non autorisé"
+ "L\'utilisateur actuel n\'est pas autorisé à effectuer cette désinstallation."
+ "Erreur"
+ "Impossible de désinstaller l\'application."
+ "Désinstaller l\'application"
+ "Désinstaller la mise à jour"
+ "%1$s fait partie de l\'application suivante :"
+ "Voulez-vous désinstaller cette application ?"
+ "Voulez-vous désinstaller cette application pour ""tous"" les utilisateurs ? L\'application et ses données seront supprimées pour ""tous"" les utilisateurs de l\'appareil."
+ "Voulez-vous désinstaller cette application pour l\'utilisateur %1$s ?"
+ "Remplacer cette application par la version d\'usine ? Toutes les données seront supprimées."
+ "Remplacer cette application par la version d\'usine ? Toutes les données seront supprimées. Tous les utilisateurs de cet appareil seront affectés, y compris ceux qui ont un profil professionnel."
+ "Désinstallations en cours"
+ "Désinstallations non abouties"
+ "Désinstallation..."
+ "Désinstallation de l\'application %1$s…"
+ "Désinstallation terminée."
+ "L\'application %1$s a été désinstallée"
+ "Échec de la désinstallation."
+ "Échec de la désinstallation de l\'application %1$s."
+ "Impossible de désinstaller une application d\'administration de l\'appareil active"
+ "Impossible de désinstaller une application d\'administration de l\'appareil active pour %1$s"
+ "Cette application nécessaire pour certains utilisateurs ou profils a été désinstallée pour d\'autres."
+ "Impossible de désinstaller l\'application, car elle est nécessaire pour votre profil."
+ "Impossible désinstaller appli, car elle est requise par administrateur appareil."
+ "Gérer les applis d\'administration de l\'appareil"
+ "Gérer les utilisateurs"
+ "Impossible de désinstaller %1$s."
+ "Un problème est survenu lors de l\'analyse du package."
+ "Nouveautés"
+ "Toutes"
+ "Confidentialité"
+ "Accès à l\'appareil"
+ "Cette mise à jour n\'exige pas de nouvelles autorisations."
+ "Refuser"
+ "Plus d\'infos"
+ "Refuser quand même"
+ "%1$s sur %2$s"
+ "Autoriser l\'application <b>%1$s</b> à %2$s ?"
+ "Toujours autoriser <b>%1$s</b> à %2$s ?"
+ "Seulement lors de l\'utilisation de l\'application"
+ "Toujours"
+ "Refuser et ne plus demander"
+ "%1$d désactivées"
+ "toutes désactivées"
+ "aucune désactivée"
+ "Autoriser"
+ "Applications"
+ "Autorisations applis"
+ "Ne plus demander"
+ "Aucune autorisation"
+ "Autorisations supplémentaires"
+ "Ouvrir les informations sur l\'application"
+
+ %1$d autre
+ %1$d autres
+
+ "Cette application a été conçue pour une ancienne version d\'Android. Si vous désactivez les autorisations, l\'application risque de ne plus fonctionner comme prévu."
+ "effectuer une action inconnue"
+ "%1$d application(s) autorisée(s) sur %2$d"
+ "Afficher les processus système"
+ "Masquer les processus système"
+ "Aucune application"
+ "Paramètres de géolocalisation"
+ "Les services de localisation pour cet appareil sont fournis via %1$s. Vous pouvez modifier l\'accès aux données de localisation dans les paramètres de localisation."
+ "Si vous refusez cette autorisation, il est possible que cela affecte certaines fonctionnalités de base de votre appareil."
+ "Activé conformément aux règles"
+ "Accès en arrière-plan désactivé conformément au règlement"
+ "Accès en arrière-plan activé conformément au règlement"
+ "Accès au premier plan activé conformément au règlement"
+ "Contrôlé par l\'administrateur"
+
+
+
+ "Toujours"
+ "Seulement lors utilisation appli"
+ "Jamais"
+ "Chargement en cours…"
+ "Toutes les autorisations"
+ "Autres autorisations de l\'application"
+ "Demande d\'autorisation"
+ "Superposition d\'écran détectée"
+ "Pour modifier ce paramètre d\'autorisation, vous devez tout d\'abord désactiver la superposition d\'écran en accédant à Paramètres > Applications."
+ "Ouvrir les paramètres"
+ "Android Wear"
+ "Opérations d\'installation et de désinstallation impossibles sur Android Wear"
+ "Sélectionner les éléments auxquels <b>%1$s</b> peut accéder"
+ "L\'application <b>%1$s</b> a été mise à jour. Sélectionnez les éléments auxquels elle peut accéder."
+ "Annuler"
+ "Continuer"
+ "Nouvelles autorisations"
+ "Autorisations actuelles"
+ "Pré-production de l\'application…"
+ "Inconnu"
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur cette tablette."
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléviseur."
+ "À des fins de sécurité, l\'installation d\'applications inconnues provenant de cette source n\'est pas autorisée sur ce téléphone."
+ "Votre téléphone et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléphone ou de toute perte de données pouvant découler de son utilisation."
+ "Votre tablette et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre tablette ou de toute perte de données pouvant découler de son utilisation."
+ "Votre téléviseur et vos données personnelles sont plus vulnérables face aux attaques d\'applications inconnues. En installant cette application, vous acceptez d\'être le seul responsable de tout dommage causé à votre téléviseur ou de toute perte de données pouvant découler de son utilisation."
+ "Continuer"
+ "Paramètres"
+ "Installer/Désinstaller les applis Wear"
+
diff --git a/packages/PackageInstaller/res/values-gl-television/strings.xml b/packages/PackageInstaller/res/values-gl-television/strings.xml
new file mode 100644
index 0000000000000..01e849878552d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Denegar e non volver preguntar"
+ "Podes cambiar esta opción máis tarde en Configuración e aplicacións"
+ "%1$s/%2$s"
+ "Mostrar aplicacións do sistema"
+ "Permisos de aplicacións"
+ "Permisos de aplicacións"
+ "Permisos de %1$s"
+ "Permisos adicionais"
+ "Permisos de %1$s"
+
diff --git a/packages/PackageInstaller/res/values-gl-watch/strings.xml b/packages/PackageInstaller/res/values-gl-watch/strings.xml
new file mode 100644
index 0000000000000..5cbb970cc0e94
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Denegar, non volver preguntar"
+ "%1$s / %2$s"
+ "Mostrar aplicacións do sistema"
+ "Cambio imposible"
+ "Si"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-gl/strings.xml b/packages/PackageInstaller/res/values-gl/strings.xml
new file mode 100644
index 0000000000000..f7bf98d82dff3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gl/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instalador de paquetes"
+ "Seguinte"
+ "Instalar"
+ "Feito"
+ "Cancelar"
+ "Instalando…"
+ "Instalando %1$s…"
+ "Aplicación instalada"
+ "Queres instalar esta aplicación? Poderá acceder a:"
+ "Queres instalar esta aplicación? Non require ningún acceso especial."
+ "Queres instalar unha actualización para esta aplicación? Non se perderán os teus datos existentes. A aplicación actualizada disporá de acceso a:"
+ "Queres instalar unha actualización para esta aplicación integrada? Non se perderán os teus datos existentes. A aplicación actualizada disporá de acceso a:"
+ "Queres instalar unha actualización para esta aplicación? Non se perderán os teus datos existentes. Non require ningún acceso especial."
+ "Queres instalar unha actualización para esta aplicación integrada? Non se perderán os teus datos existentes. Non require ningún acceso especial."
+ "Aplicación non instalada"
+ "Bloqueouse a instalación do paquete."
+ "A aplicación non se instalou porque o paquete presenta un conflito cun paquete existente."
+ "A aplicación non se instalou porque a aplicación non é compatible coa tableta."
+ "Esta aplicación non é compatible coa túa televisión."
+ "A aplicación non se instalou porque a aplicación non é compatible co teléfono."
+ "A aplicación non se instalou porque parece que o paquete non é válido."
+ "Non se puido instalar %1$s na túa tableta."
+ "%1$s non se puido instalar na túa televisión."
+ "Non se puido instalar %1$s no teu teléfono."
+ "Abrir"
+ "O teu administrador non permite a instalación de aplicacións obtidas a partir de fontes descoñecidas"
+ "Este usuario non pode instalar aplicacións descoñecidas"
+ "Este usuario non ten permiso para instalar aplicacións"
+ "Aceptar"
+ "Xestionar aplicacións"
+ "Espazo esgotado"
+ "Non se puido instalar %1$s. Libera espazo e téntao de novo."
+ "Non se encontrou a aplicación"
+ "Non se atopou a aplicación na lista de aplicacións instaladas."
+ "Non permitido"
+ "O usuario actual non pode realizar esta desinstalación."
+ "Erro"
+ "Non se puido desinstalar a aplicación."
+ "Desinstalar aplicación"
+ "Desinstalar actualización"
+ "%1$s forma parte da seguinte aplicación:"
+ "Queres desinstalar esta aplicación?"
+ "Queres desinstalar esta aplicación para ""todos"" os usuarios? A aplicación e os seus datos eliminaranse de ""todos"" os usuarios do dispositivo."
+ "Queres desinstalar esta aplicación para o usuario %1$s?"
+ "Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos."
+ "Queres substituír esta aplicación pola versión que viña de fábrica? Eliminaranse todos os datos. Isto afectará a todos os usuarios do dispositivo, incluídos os que teñan perfís de traballo."
+ "Desintalacións en curso"
+ "Erros nas desinstalacións"
+ "Desinstalando…"
+ "Desinstalando %1$s…"
+ "Desinstalación finalizada"
+ "Desinstalouse %1$s"
+ "Desinstalación incorrecta"
+ "A desinstalación de %1$s non se realizou correctamente."
+ "Non se puido desinstalar a aplicación de administración de dispositivos activa"
+ "Non se puido desinstalar a aplicación de administración de dispositivos activa para %1$s"
+ "A aplicación é necesaria para algúns usuarios ou perfís e estaba desinstalada para outros"
+ "O teu perfil necesita esta aplicación e non se pode desinstalar."
+ "O administrador do teu dispositivo necesita esta aplicación e non se pode desinstalar."
+ "Xestionar apps de administración de dispositivos"
+ "Administrar usuarios"
+ "Non se puido desinstalar %1$s."
+ "Produciuse un problema ao analizar o paquete."
+ "Novo"
+ "Todos"
+ "Privacidade"
+ "Acceso dispositivo"
+ "Esta actualización non require novos permisos."
+ "Denegar"
+ "Máis información"
+ "Denegar igualmente"
+ "%1$s de %2$s"
+ "Queres permitir á aplicación <b>%1$s</b> %2$s?"
+ "Permitir sempre á aplicación <b>%1$s</b> %2$s?"
+ "Só ao usar a aplicación"
+ "Sempre"
+ "Denegar e non volver preguntar"
+ "%1$d desactivados"
+ "todos desactivados"
+ "ningún desactivado"
+ "Permitir"
+ "Aplicacións"
+ "Permisos de aplicacións"
+ "Non preguntar de novo"
+ "Sen permisos"
+ "Permisos adicionais"
+ "Abrir información da aplicación"
+
+ %1$d permisos máis
+ %1$d permiso máis
+
+ "Esta aplicación deseñouse para unha versión anterior de Android. Denegar o permiso pode provocar que non funcione como está previsto."
+ "realiza unha acción descoñecida"
+ "%1$d de %2$d aplicacións con permiso"
+ "Mostrar sistema"
+ "Ocultar sistema"
+ "Ningunha aplicación"
+ "Configuración da localización"
+ "%1$s é un fornecedor de servizos de localización para este dispositivo. O acceso de localización pode modificarse desde a configuración de localización."
+ "Se denegas este permiso, é posible que as funcións básicas do teu dispositivo deixen de funcionar segundo o previsto."
+ "Aplicado pola política"
+ "O acceso en segundo plano está desactivado pola política"
+ "O acceso en segundo plano está activado pola política"
+ "O acceso en primeiro plano está activado pola política"
+ "Opción controlada polo administrador"
+
+
+
+ "Sempre"
+ "Só ao usar a aplicación"
+ "Nunca"
+ "Cargando…"
+ "Todos os permisos"
+ "Outras funcionalidades da aplicación"
+ "Solicitude de permiso"
+ "Detectouse unha superposición na pantalla"
+ "Para cambiar a configuración deste permiso, primeiro tes que desactivar a superposición na pantalla, en Configuración > Aplicacións"
+ "Abrir configuración"
+ "Android Wear"
+ "As accións de instalar e desinstalar non son compatibles con Wear."
+ "Seleccionar os permisos de acceso que queres dar a <b>%1$s</b>"
+ "Actualizouse a aplicación <b>%1$s</b>. Selecciona os permisos de acceso que lle queres dar."
+ "Cancelar"
+ "Continuar"
+ "Novos permisos"
+ "Permisos actuais"
+ "Probando aplicación…"
+ "Descoñecida"
+ "Por cuestións de seguranza, na tableta non se poden instalar aplicacións descoñecidas procedentes desta fonte."
+ "Por cuestións de seguranza, na televisión non se poden instalar aplicacións descoñecidas procedentes desta fonte."
+ "Por cuestións de seguranza, no teléfono non se poden instalar aplicacións descoñecidas procedentes desta fonte."
+ "O teléfono e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados no teléfono ou da perda dos datos que se poidan derivar do seu uso."
+ "A tableta e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na tableta ou da perda dos datos que se poidan derivar do seu uso."
+ "A televisión e os datos persoais son máis vulnerables aos ataques de aplicacións descoñecidas. Ao instalar esta aplicación, aceptas que es responsable dos danos ocasionados na televisión ou da perda dos datos que se poidan derivar do seu uso."
+ "Continuar"
+ "Configuración"
+ "Instalando/desinstalando apps Wear"
+
diff --git a/packages/PackageInstaller/res/values-gu-television/strings.xml b/packages/PackageInstaller/res/values-gu-television/strings.xml
new file mode 100644
index 0000000000000..b0a40b6ceafe3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "નકારો અને ફરીથી પૂછશો નહીં"
+ "તમે પછીથી આને સેટિંગ્સ > એપ્લિકેશન્સમાં બદલી શકો છો"
+ "%1$s / %2$s"
+ "સિસ્ટમ ઍપ્લિકેશનો બતાવો"
+ "ઍપ્લિકેશન પરવાનગીઓ"
+ "ઍપ્લિકેશન પરવાનગીઓ"
+ "%1$s પરવાનગીઓ"
+ "વધારાની પરવાનગીઓ"
+ "%1$s પરવાનગીઓ"
+
diff --git a/packages/PackageInstaller/res/values-gu-watch/strings.xml b/packages/PackageInstaller/res/values-gu-watch/strings.xml
new file mode 100644
index 0000000000000..6e83cf2e70989
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "નકારો, ફરીથી પૂછશો નહીં"
+ "%1$s / %2$s"
+ "સિસ્ટમ ઍપ્લિકેશનો બતાવો"
+ "બદલી શકતાં નથી"
+ "હા"
+ "રદ કરો"
+
diff --git a/packages/PackageInstaller/res/values-gu/strings.xml b/packages/PackageInstaller/res/values-gu/strings.xml
new file mode 100644
index 0000000000000..5e0a2b367b41b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-gu/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "પૅકેજ ઇન્સ્ટોલર"
+ "આગલું"
+ "ઇન્સ્ટોલ કરો"
+ "થઈ ગયું"
+ "રદ કરો"
+ "ઇન્સ્ટોલ કરી રહ્યું છે…"
+ "%1$sને ઇન્સ્ટૉલ કરી રહ્યાં છીએ…"
+ "ઍપ્લિકેશન ઇન્સ્ટોલ કરી."
+ "શું તમે આ ઍપ્લિકેશન ઇન્સ્ટોલ કરવા માંગો છો? તે આની ઍક્સેસ મેળવશે:"
+ "શું તમે આ એપ્લિકેશનને ઇન્સ્ટોલ કરવા માંગો છો? તેને કોઈપણ વિશિષ્ટ ઍક્સેસની જરૂર નથી."
+ "શું તમે આ અસ્તિત્વમાંની એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. અપડેટ કરેલ એપ્લિકેશનને આની ઍક્સેસ મળશે:"
+ "શું તમે આ બિલ્ટ-ઇન એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. અપડેટ કરેલ એપ્લિકેશનને આની ઍક્સેસ મળશે:"
+ "શું તમે આ અસ્તિત્વમાંની એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. તેને કોઈ વિશિષ્ટ ઍક્સેસની જરૂર હોતી નથી."
+ "શું તમે આ બિલ્ટ-ઇન એપ્લિકેશનના અપડેટને ઇન્સ્ટોલ કરવા માગો છો? તમારો અસ્તિત્વમાંનો ડેટા ગુમ થશે નહીં. તેને કોઈ વિશિષ્ટ ઍક્સેસની જરૂર હોતી નથી."
+ "ઍપ્લિકેશન ઇન્સ્ટોલ કરેલ નથી."
+ "પૅકેજને ઇન્સ્ટૉલ થવાથી અવરોધિત કરવામાં આવ્યું હતું."
+ "પૅકેજનો અસ્તિત્વમાંના પૅકેજ સાથે વિરોધાભાસ હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."
+ "તમારા ટેબ્લેટ સાથે ઍપ્લિકેશન સુસંગત ન હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."
+ "આ અૅપ્લિકેશન તમારા ટીવી સાથે સુસંગત નથી."
+ "તમારા ફોન સાથે ઍપ્લિકેશન સુસંગત ન હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."
+ "પૅકેજ અમાન્ય લાગી રહ્યું હોવાને કારણે ઍપ્લિકેશન ઇન્સ્ટૉલ થઈ નથી."
+ "તમારા ટેબ્લેટ પર %1$s ઇન્સ્ટોલ કરી શકાયું નથી."
+ "તમારા ટીવી પર %1$s ઇન્સ્ટોલ કરી શકાયું નથી."
+ "તમારા ફોન પર %1$s ઇન્સ્ટોલ કરી શકાયું નથી."
+ "ખોલો"
+ "તમારા વ્યવસ્થાપક અજાણ્યા સ્રોતોથી મેળવેલ ઍપ્લિકેશનોના ઇન્સ્ટૉલેશનની મંજૂરી આપતા નથી"
+ "આ વપરાશકર્તા અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરી શકશે નહીં"
+ "આ વપરાશકર્તાને ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી"
+ "ઓકે"
+ "એપ્લિકેશન્સનું સંચાલન કરો"
+ "જગ્યાની બહાર"
+ "%1$s ઇન્સ્ટોલ કરી શકાઈ નથી. થોડી સ્પેસ ખાલી કરો અને ફરીથી પ્રયાસ કરો."
+ "ઍપ્લિકેશન મળી નથી"
+ "ઇન્સ્ટોલ કરેલ ઍપ્લિકેશન્સની સૂચિમાં ઍપ્લિકેશન મળી નહોતી."
+ "મંજૂરી નથી"
+ "વર્તમાન વપરાશકર્તાને આ અનઇન્સ્ટૉલેશન કરવાની મંજૂરી નથી."
+ "ભૂલ"
+ "ઍપ્લિકેશન અનઇન્સ્ટૉલ કરી શકાઈ નહીં."
+ "ઍપ્લિકેશન અનઇન્સ્ટોલ કરો"
+ "અપડેટ અનઇન્સ્ટોલ કરો"
+ "%1$s એ નીચેની એપ્લિકેશનનો એક ભાગ છે:"
+ "શું તમે આ એપ્લિકેશનને અનઇન્સ્ટોલ કરવા માંગો છો?"
+ "શું તમે ""તમામ"" વપરાશકર્તાઓ માટે આ ઍપ્લિકેશનને અનઇન્સ્ટોલ કરવા માગો છો? ઉપકરણ પરના ""તમામ"" વપરાશકર્તાઓમાંથી ઍપ્લિકેશન અને તેનો ડેટા દૂર કરવામાં આવશે."
+ "શું તમે %1$s વપરાશકર્તા માટે આ એપ્લિકેશનને અનઇન્સ્ટોલ કરવા માગો છો?"
+ "આ ઍપ્લિકેશનને ફેક્ટરી સંસ્કરણથી બદલીએ? તમામ ડેટા દૂર કરવામાં આવશે."
+ "આ ઍપ્લિકેશનને ફેક્ટરી સંસ્કરણથી બદલીએ? તમામ ડેટા દૂર કરવામાં આવશે. આનાથી કાર્ય પ્રોફાઇલ્સ સાથેના વપરાશકર્તાઓ સહિત આ ઉપકરણના તમામ વપરાશકર્તાઓ પ્રભાવિત થશે."
+ "ચાલી રહેલા અનઇન્સ્ટૉલ"
+ "નિષ્ફળ થયેલા અનઇન્સ્ટૉલ"
+ "અનઇન્સ્ટોલ કરી રહ્યું છે..."
+ "%1$s ને અનઇન્સ્ટૉલ કરી રહ્યાં છે…"
+ "અનઇન્સ્ટોલ કરો સમાપ્ત થયું."
+ "%1$s અનઇન્સ્ટૉલ કર્યું"
+ "અનઇન્સ્ટોલ કરવું અસફળ રહ્યું."
+ "%1$s ને અનઇન્સ્ટૉલ કરવું અસફળ રહ્યું."
+ "સક્રિય ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોને અનઇન્સ્ટૉલ કરી શકાતી નથી"
+ "%1$s માટે સક્રિય ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોને અનઇન્સ્ટૉલ કરી શકાતી નથી"
+ "આ અૅપ્લિકેશન અમુક વપરાશકર્તાઓ અથવા પ્રોફાઇલ્સ માટે જરૂરી છે અને તે અન્ય લોકો માટે અનઇન્સ્ટૉલ કરી હતી"
+ "તમારી કાર્યાલયની પ્રોફાઇલ માટે ઍપ્લિકેશન જરૂરી છે અને અનઇન્સ્ટૉલ કરી શકાતી નથી."
+ "આ ઍપ્લિકેશન તમારા ઉપકરણ વ્યવસ્થાપક માટે આવશ્યક છે અને તે અનઇન્સ્ટોલ કરી શકાતી નથી."
+ "ઉપકરણ વ્યવસ્થાપક ઍપ્લિકેશનોનું સંચાલન કરો"
+ "વપરાશકર્તાઓનું સંચાલન કરો"
+ "%1$s અનઇન્સ્ટોલ કરી શકાઈ નથી."
+ "પૅકેજનું વિશ્લેષણ કરવામાં એક સમસ્યા આવી હતી."
+ "નવું"
+ "તમામ"
+ "ગોપનીયતા"
+ "ઉપકરણ ઍક્સેસ"
+ "આ અપડેટને કોઈ નવી પરવાનગીઓની જરૂર નથી."
+ "નકારો"
+ "વધુ માહિતી"
+ "કોઇપણ રીતે નકારો"
+ "%2$s માંથી %1$s"
+ "<b>%1$s</b> ને %2$s મંજૂરી આપીએ?"
+ "<b>%1$s</b>ને હંમેશા %2$sની મંજૂરી આપીએ?"
+ "માત્ર ઍપનો ઉપયોગ કરતી વખતે જ"
+ "હંમેશા"
+ "નકારો અને ફરીથી પૂછશો નહીં"
+ "%1$d અક્ષમ કરી"
+ "તમામ અક્ષમ કરી"
+ "કોઈપણ અક્ષમ કરેલ નથી"
+ "મંજૂરી આપો"
+ "ઍપ્લિકેશનો"
+ "ઍપ્લિકેશન પરવાનગીઓ"
+ "ફરીથી પૂછશો નહીં"
+ "કોઈ પરવાનગીઓ નથી"
+ "વધારાની પરવાનગીઓ"
+ "ઍપ માહિતી ખોલો"
+
+ %1$d વધુ
+ %1$d વધુ
+
+ "આ ઍપ્લિકેશન Android ના જુના સંસ્કરણ માટે તૈયાર કરવામાં આવી હતી. પરવાનગી નકારવાથી તે ધાર્યા પ્રમાણે બિલકુલ કાર્ય કરશે નહી."
+ "એક અજાણી ક્રિયા કરો"
+ "%2$d માંથી %1$d એપ્લિકેશન્સને મંજૂરી છે"
+ "સિસ્ટમ બતાવો"
+ "સિસ્ટમ છુપાવો"
+ "કોઇ ઍપ્લિકેશનો નથી"
+ "સ્થાન સેટિંગ્સ"
+ "%1$s એ આ ઉપકરણ માટે સ્થાન સેવાઓના પ્રદાતા છે. સ્થાન સેટિંગ્સમાંથી સ્થાન ઍક્સેસ સંશોધિત કરી શકાય છે."
+ "જો તમે આ પરવાનગી નકારો છો, તો તમારા ઉપકરણની મૂળભૂત સુવિધાઓ અપેક્ષા પ્રમાણે કાર્ય કરી શકશે નહીં."
+ "નીતિ દ્વારા લાગુ"
+ "નીતિ દ્વારા બૅકગ્રાઉન્ડ ઍક્સેસને બંધ કરવામાં આવ્યો છે"
+ "નીતિ દ્વારા બૅકગ્રાઉન્ડ ઍક્સેસને ચાલુ કરવામાં આવ્યો છે"
+ "નીતિ દ્વારા ફૉરગ્રાઉન્ડ ઍક્સેસને ચાલુ કરવામાં આવ્યો છે"
+ "વ્યવસ્થાપક દ્વારા નિયંત્રિત"
+
+
+
+ "હંમેશા"
+ "માત્ર ઍપનો ઉપયોગ કરતી વખતે જ"
+ "ક્યારેય નહીં"
+ "લોડ કરી રહ્યું છે..."
+ "બધી પરવાનગીઓ"
+ "અન્ય ઍપ્લિકેશન ક્ષમતાઓ"
+ "પરવાનગીની વિનંતી"
+ "સ્ક્રીન ઓવરલે મળ્યું"
+ "આ પરવાનગી સેટિંગ બદલવા માટે, તમારે પહેલા સેટિંગ્સ > Apps માંથી સ્ક્રીન ઓવરલે બંધ કરવું પડશે"
+ "સેટિંગ્સ ખોલો"
+ "Android Wear"
+ "Wear પર ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ ક્રિયાઓ સમર્થિત નથી."
+ "<b>%1$s</b> ને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો"
+ "<b>%1$s</b> અપડેટ કરવામાં આવી છે. આ ઍપ્લિકેશનને શેના ઍક્સેસ માટેની મંજૂરી આપવી તે પસંદ કરો."
+ "રદ કરો"
+ "ચાલુ રાખો"
+ "નવી પરવાનગીઓ"
+ "વર્તમાન પરવાનગીઓ"
+ "ઍપ્લિકેશન અમલમં છે..."
+ "અજાણી"
+ "તમારી સુરક્ષા માટે, તમારા ટૅબ્લેટને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."
+ "તમારી સુરક્ષા માટે, તમારા ટીવીને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."
+ "તમારી સુરક્ષા માટે, તમારા ફોનને આ સ્રોત પરથી અજાણી ઍપ્લિકેશનો ઇન્સ્ટૉલ કરવાની મંજૂરી નથી."
+ "તમારો ફોન અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ફોનને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."
+ "તમારું ટૅબ્લેટ અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટૅબ્લેટને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."
+ "તમારું ટીવી અને વ્યક્તિગત ડેટા અજાણી ઍપ્લિકેશનો દ્વારા હુમલા માટે વધુ સંવેદનશીલ છે. આ ઍપ્લિકેશન ઇન્સ્ટૉલ કરીને તમે સંમત થાઓ છો કે આનો ઉપયોગ કરવાથી તમારા ટીવીને થતી કોઈપણ હાનિ અથવા ડેટાના નુકસાન માટે તમે જવાબદાર છો."
+ "ચાલુ રાખો"
+ "સેટિંગ્સ"
+ "એમ્બેડ ઍપ્લિકેશનો ઇન્સ્ટૉલ/અનઇન્સ્ટૉલ"
+
diff --git a/packages/PackageInstaller/res/values-hi-television/strings.xml b/packages/PackageInstaller/res/values-hi-television/strings.xml
new file mode 100644
index 0000000000000..2acd9c50c9127
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "मना करें और फिर से ना पूछें"
+ "आप इसे बाद में सेटिंग > ऐप्स में बदल सकते हैं"
+ "%1$s / %2$s"
+ "सिस्टम ऐप्स दिखाएं"
+ "ऐप्लिकेशन अनुमतियां"
+ "ऐप्लिकेशन अनुमतियां"
+ "%1$s अनुमतियां"
+ "और अनुमतियां"
+ "%1$s अनुमतियां"
+
diff --git a/packages/PackageInstaller/res/values-hi-watch/strings.xml b/packages/PackageInstaller/res/values-hi-watch/strings.xml
new file mode 100644
index 0000000000000..f7adb0ce53181
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "मना करें, फिर से ना पूछें"
+ "%1$s / %2$s"
+ "सिस्टम ऐप्स दिखाएं"
+ "बदला नहीं जा सकता"
+ "हां"
+ "रद्द करें"
+
diff --git a/packages/PackageInstaller/res/values-hi/strings.xml b/packages/PackageInstaller/res/values-hi/strings.xml
new file mode 100644
index 0000000000000..5ddddbe83398d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hi/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "पैकेज इंस्टॉलर"
+ "आगे"
+ "इंस्टॉल करें"
+ "हो गया"
+ "रद्द करें"
+ "इंस्टॉल कर रहा है…"
+ "%1$s इंस्टॉल हो रहा है…"
+ "ऐप्स इंस्टॉल हो गया."
+ "क्या आप इस ऐप्स को इंस्टॉल करना चाहते हैं? इससे यहां पर पहुंच प्राप्त होगी:"
+ "क्या आप इस ऐप्स को इंस्टॉल करना चाहते हैं? इसके लिए किसी विशेष पहुंच की आवश्यकता नहीं है."
+ "क्या आप इस मौजूदा ऐप के बारे में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा गुम नहीं होगा. अपडेट किये गए ऐप से आपको इन पर पहुंच मिलेगी:"
+ "क्या आप इस बिल्ट-इन ऐप के बारे में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा गुम नहीं होगा. नई जानकारी वाले ऐप से आपको इन पर मिलेगी:"
+ "क्या आप इस मौजूदा ऐप में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा बना रहेगा. इसे किसी खास पहुंच की ज़रुरत नहीं होती."
+ "क्या आप इस मौजूदा ऐप में नई जानकारी इंस्टॉल करना चाहते हैं? आपका मौजूदा डेटा बना रहेगा. इसे किसी खास पहुंच की ज़रुरत नहीं होती."
+ "ऐप्स इंस्टॉल नहीं हुआ."
+ "पैकेज को इंस्टॉल होने से अवरुद्ध किया हुआ है."
+ "ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज का किसी मौजूदा पैकेज से विरोध है."
+ "ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि ऐप्लिकेशन आपके टैबलेट से संगत नहीं है."
+ "यह ऐप आपके टीवी के संगत नहीं है."
+ "ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि ऐप्लिकेशन आपके फ़ोन से संगत नहीं है."
+ "ऐप्लिकेशन इंस्टॉल नहीं हुआ क्योंकि पैकेज अमान्य लग रहा है."
+ "%1$s को आपके टैबलेट पर इंस्टॉल नहीं किया जा सका."
+ "%1$s को आपके टीवी पर इंस्टॉल नहीं किया जा सकता."
+ "%1$s को आपके फ़ोन पर इंस्टॉल नहीं किया जा सका."
+ "खोलें"
+ "आपका व्यवस्थापक अनजान स्रोतों से मिलने वाले ऐप्लिकेशन को इंस्टॉल करने की अनुमति नहीं देता है"
+ "यह उपयोगकर्ता अनजान ऐप्लिकेशन इंस्टॉल नहीं कर सकता"
+ "इस उपयोगकर्ता ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है"
+ "ठीक है"
+ "ऐप्स प्रबंधित करें"
+ "जगह नहीं है"
+ "%1$s को इंस्टॉल नहीं किया जा सका. थोड़ी जगह खाली करें और फिर से कोशिश करें."
+ "ऐप्स नहीं मिला"
+ "ऐप्स , इंस्टॉल किए गए ऐप्स की सूची में नहीं मिला था."
+ "अनुमति नहीं है"
+ "मौजूदा उपयोगकर्ता को यह अनइंस्टॉल करने की अनुमति नहीं है"
+ "गड़बड़ी"
+ "ऐप्लिकेशन अनइंस्टॉल नहीं किया जा सका."
+ "ऐप्स अनइंस्टॉल करें"
+ "अपडेट अनइंस्टॉल करें"
+ "%1$s निम्न ऐप्स का भाग है:"
+ "क्या आप इस ऐप्स को अनइंस्टॉल करना चाहते हैं?"
+ "क्या आप इस ऐप्स को ""सभी"" उपयोगकर्ताओं के लिए अनइंस्टॉल करना चाहते हैं? ऐप्स और उसके डेटा को डिवाइस पर ""सभी"" उपयोगकर्ताओं से निकाल दिया जाएगा."
+ "क्या आप उपयोगकर्ता %1$s के लिए इस ऐप को अनइंस्टॉल करना चाहते हैं?"
+ "इस ऐप्लिकेशन को फ़ैक्टरी वर्शन से बदलें? सभी डेटा निकाल दिया जाएगा."
+ "इस ऐप्लिकेशन को फ़ैक्ट्री वर्शन से बदलें? पूरा डेटा निकाल दिया जाएगा. इसका इस डिवाइस के सभी उपयोगकर्ताओं पर असर पड़ेगा, जिनमें कार्य प्रोफ़ाइल वाले उपयोगकर्ता शामिल हैं."
+ "वे अनइंस्टॉल जो चल रहे हैं"
+ "वे अनइंस्टॉल जो सफल नहीं रहे"
+ "अनइंस्टॉल कर रहा है…"
+ "%1$s अनइंस्टॉल किया जा रहा है…"
+ "अनइंस्टॉल करना पूर्ण हो गया."
+ "%1$s अनइंस्टॉल किया गया"
+ "अनइंस्टॉल करना विफल."
+ "%1$s को अनइंस्टॉल करना असफल."
+ "सक्रिय डिवाइस व्यवस्थापक ऐप्लिकेशन को अनइंस्टॉल नहीं किया जा सकता"
+ "%1$s के लिए सक्रिय डिवाइस व्यवस्थापक ऐप्लिकेशन को अनइंस्टॉल नहीं किया जा सकता"
+ "यह ऐप्लिकेशन कुछ उपयोगकर्ताओं या प्रोफ़ाइल हेतु आवश्यक है और अन्य हेतु अनइंस्टॉल हो गया है"
+ "आपकी कार्य प्रोफ़ाइल के लिए यह ऐप्लिकेशन आवश्यक है और उसे अनइंस्टॉल नहीं किया जा सकता."
+ "आपके डिवाइस व्यवस्थापक के लिए यह ऐप्स जरूरी है व इसे अनइंस्टॉल नहीं किया जा सकता."
+ "डिवाइस व्यवस्थापक ऐप्लिकेशन प्रबंधित करें"
+ "उपयोगकर्ताओं को प्रबंधित करें"
+ "%1$s को अनइंस्टॉल नहीं किया जा सका."
+ "पैकेज को पार्स करने में कोई समस्या थी."
+ "नया"
+ "सभी"
+ "निजता"
+ "डिवाइस पहुंच"
+ "इस अपडेट लिए अनुमति की ज़रुरत नहीं है."
+ "अस्वीकार करें"
+ "अधिक जानकारी"
+ "फिर भी अस्वीकार करें"
+ "%2$s में से %1$s"
+ "<b>%1$s</b> को %2$s की अनुमति दें?"
+ "<b>%1$s</b> को हमेशा %2$s की अनुमति दें?"
+ "सिर्फ़ ऐप्लिकेशन इस्तेमाल करते समय"
+ "हमेशा"
+ "अनुमति न दें और दोबारा न पूछें"
+ "%1$d अक्षम"
+ "सभी अक्षम हैं"
+ "कोई भी अक्षम नहीं है"
+ "अनुमति दें"
+ "ऐप्लिकेशन"
+ "ऐप्लिकेशन अनुमतियां"
+ "फिर से न पूछें"
+ "कोई अनुमति नहीं"
+ "और अनुमतियां"
+ "ऐप्लिकेशन से जुड़ी जानकारी देखें"
+
+ %1$d और
+ %1$d और
+
+ "इस ऐप को Android के पुराने वर्शन के लिए डिज़ाइन किया गया था. अनुमति अस्वीकार करने पर हो सकता है कि फ़ंक्शन लक्षित रूप से काम नहीं करे."
+ "कोई अज्ञात कार्रवाई करें"
+ "%2$d में से %1$d ऐप्लिकेशन को अनुमति है"
+ "सिस्टम दिखाएं"
+ "सिस्टम छिपाएं"
+ "कोई ऐप्स नहीं"
+ "जगह की सेटिंग"
+ "%1$s इस डिवाइस के लिए जगह की जानकारी उपलब्ध कराता है. जगह की पहुंच (एक्सेस) को जगह की सेटिंग से बदला जा सकता है."
+ "यदि आप इस अनुमति को अस्वीकार करते हैं, तो हो सकता है कि आपके डिवाइस की मूलभूत सुविधाएं लक्षित कार्य ना कर पाएं."
+ "नीति द्वारा लागू"
+ "नीति के मुताबिक बैकग्राउंड एक्सेस बंद किया गया"
+ "नीति के मुताबिक बैकग्राउंड एक्सेस चालू किया गया"
+ "नीति के मुताबिक फ़ोरग्राउंड एक्सेस चालू किया गया"
+ "इसका नियंत्रण एडमिन के पास है"
+
+
+
+ "हमेशा"
+ "ऐप्लिकेशन इस्तेमाल करते समय"
+ "कभी नहीं"
+ "लोड हो रहा है…"
+ "सभी अनुमतियां"
+ "अन्य ऐप कार्यक्षमताएं"
+ "अनुमति का अनुरोध"
+ "स्क्रीन ओवरले मिला"
+ "इस अनुमति सेटिंग को बदलने के लिए, आपको पहले सेटिंग > ऐप, से स्क्रीन ओवरले को बंद करना होगा"
+ "सेटिंग खोलें"
+ "Android Wear"
+ "इंस्टॉल/अनइंस्टॉल किए जाने की कार्रवाइयां Wear पर समर्थित नहीं हैं."
+ "यह चुनें कि <b>%1$s</b> को किस-किस चीज पर पहुंचने देना चाहते हैं"
+ "<b>%1$s</b> को अपडेट कर दिया गया है. यह चुनें कि इस ऐप्लिकेशन को किस-किस चीज तक पहुंचने देना चाहते हैं."
+ "रद्द करें"
+ "जारी रखें"
+ "नई अनुमतियां"
+ "वर्तमान अनुमतियां"
+ "ऐप्लिकेशन चरणबद्ध किया जा रहा है…"
+ "अज्ञात"
+ "आपकी सुरक्षा के लिए, आपके टैबलेट को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."
+ "आपकी सुरक्षा के लिए, आपके टीवी को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."
+ "आपकी सुरक्षा के लिए, आपके फ़ोन को इस स्रोत से आने वाले अनजान ऐप्लिकेशन इंस्टॉल करने की अनुमति नहीं है."
+ "आपका फ़ोन और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके फ़ोन को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."
+ "आपका टैबलेट और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके टैबलेट को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."
+ "आपका टीवी और व्यक्तिगत डेटा अज्ञात ऐप्लिकेशन के हमले के प्रति अधिक संवेदनशील हैं. इस ऐप्लिकेशन को इंस्टॉल करके आप सहमति देते हैं कि इसके उपयोग के चलते आपके टीवी को होने वाले किसी भी नुकसान या डेटा की हानि के लिए आप ज़िम्मेदार हैं."
+ "जारी रखें"
+ "सेटिंग"
+ "वियर ऐप इंस्टॉल/अनइंस्टॉल हो रहे हैं"
+
diff --git a/packages/PackageInstaller/res/values-hr-television/strings.xml b/packages/PackageInstaller/res/values-hr-television/strings.xml
new file mode 100644
index 0000000000000..ba363f4c0c25f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Odbij i više ne pitaj"
+ "To možete kasnije promijenili u odjeljku Postavke > Aplikacije"
+ "%1$s/%2$s"
+ "Prikaži aplikacije sustava"
+ "Dopuštenja aplikacije"
+ "Dopuštenja aplikacije"
+ "Dopuštenja – %1$s"
+ "Dodatna dopuštenja"
+ "Dopuštenja – %1$s"
+
diff --git a/packages/PackageInstaller/res/values-hr-watch/strings.xml b/packages/PackageInstaller/res/values-hr-watch/strings.xml
new file mode 100644
index 0000000000000..cd44eee076e7b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Ne, više ne pitaj"
+ "%1$s/%2$s"
+ "Prikaži aplikacije sustava"
+ "Promjena nemoguća"
+ "Da"
+ "Odustani"
+
diff --git a/packages/PackageInstaller/res/values-hr/strings.xml b/packages/PackageInstaller/res/values-hr/strings.xml
new file mode 100644
index 0000000000000..3884ae594bd15
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hr/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Alat za instaliranje paketa"
+ "Sljedeća"
+ "Instaliraj"
+ "Gotovo"
+ "Odustani"
+ "Instaliranje..."
+ "Instaliranje paketa %1$s…"
+ "Aplikacija je instalirana."
+ "Želite li instalirati ovu aplikaciju? Aplikacija će moći sljedeće:"
+ "Želite li instalirati ovu aplikaciju? Aplikacija ne zahtijeva nikakav poseban pristup."
+ "Želite li instalirati ažuriranje postojeće aplikacije? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija dobit će pristup sljedećem:"
+ "Želite li instalirati ažuriranje za ovu ugrađenu aplikaciju? Vaši postojeći podaci neće biti izgubljeni. Ažurirana aplikacija dobit će pristup sljedećem:"
+ "Želite li instalirati ažuriranje postojeće aplikacije? Vaši postojeći podaci neće se izgubiti. Nije potreban nikakav poseban pristup."
+ "Želite li instalirati ažuriranje te ugrađene aplikacije? Vaši postojeći podaci neće se izgubiti. Nije potreban nikakav poseban pristup."
+ "Aplikacija nije instalirana."
+ "Instaliranje paketa blokirano je."
+ "Aplikacija koja nije instalirana kao paket u sukobu je s postojećim paketom."
+ "Aplikacija koja nije instalirana kao aplikacija nije kompatibilna s vašim tabletom."
+ "Aplikacija nije kompatibilna s vašim televizorom."
+ "Aplikacija koja nije instalirana kao aplikacija nije kompatibilna s vašim telefonom."
+ "Aplikacija koja nije instalirana kao paket vjerojatno nije važeća."
+ "Aplikaciju %1$s nije moguće instalirati na ovo tabletno računalo."
+ "Nije bilo moguće instalirati aplikaciju %1$s na vaš televizor."
+ "Aplikaciju %1$s nije moguće instalirati na vaš telefon."
+ "Otvori"
+ "Vaš administrator ne dopušta instaliranje aplikacija iz nepoznatih izvora"
+ "Ovaj korisnik ne može instalirati nepoznate aplikacije"
+ "Ovaj korisnik nema dopuštenje za instaliranje aplikacija"
+ "U redu"
+ "Upravljanje aplikacijama"
+ "Nema dovoljno mjesta"
+ "Aplikaciju %1$s nije moguće instalirati. Oslobodite dio prostora i pokušajte ponovo."
+ "Aplikacija nije pronađena"
+ "Na popisu instaliranih aplikacija ova aplikacija nije pronađena."
+ "Nije dopušteno"
+ "Trenutačni korisnik nema dopuštenje za to deinstaliranje."
+ "Pogreška"
+ "Deinstaliranje aplikacije nije uspjelo."
+ "Deinstaliraj aplikaciju"
+ "Deinstalacija ažuriranja"
+ "Aktivnost %1$s dio je sljedeće aplikacije:"
+ "Želite li deinstalirati ovu aplikaciju?"
+ "Želite li deinstalirati tu aplikaciju za ""sve"" korisnike? Aplikacija i njezini podaci bit će uklonjeni sa ""svih"" korisnika na uređaju."
+ "Želite li deinstalirati tu aplikaciju za korisnika %1$s?"
+ "Želite li tu aplikaciju zamijeniti tvorničkom verzijom? Izgubit ćete sve podatke."
+ "Želite li tu aplikaciju zamijeniti tvorničkom verzijom? Izgubit ćete sve podatke. To se odnosi na sve korisnike uređaja, uključujući one s radnim profilima."
+ "Deinstaliranja u tijeku"
+ "Neuspjela deinstaliranja"
+ "Deinstaliranje..."
+ "Deinstaliranje aplikacije %1$s…"
+ "Deinstalacija je završena."
+ "Aplikacija %1$s deinstalirana"
+ "Deinstalacija nije uspjela."
+ "Deinstaliranje aplikacije %1$s nije uspjelo."
+ "Deinstaliranje aktivne aplikacije administratora uređaja nije uspjelo"
+ "Nije uspjelo deinstaliranje aktivne aplikacije administratora uređaja za %1$s"
+ "Ta je aplikacija obavezna za neke korisnike ili profile, deinstalirana je za ostale"
+ "Ta je aplikacija potrebna za vaš profil i ne može se deinstalirati."
+ "Ta je aplikacija neophodna administratoru uređaja i nije ju moguće deinstalirati."
+ "Upravljaj aplikacijama administratora uređaja"
+ "Upravljaj korisnicima"
+ "Aplikaciju %1$s nije moguće instalirati."
+ "Došlo je do problema pri analiziranju paketa."
+ "Novo"
+ "Sve"
+ "Privatnost"
+ "Pristup uređaja"
+ "Ovo ažuriranje ne zahtijeva nove dozvole."
+ "Odbij"
+ "Više informacija"
+ "Svejedno odbij"
+ "%1$s od %2$s"
+ "Želite li aplikaciji <b>%1$s</b> dopustiti da može %2$s?"
+ "Želite li uvijek dopustiti aplikaciji <b>%1$s</b> sljedeće: %2$s?"
+ "Samo dok se aplikacija koristi"
+ "Uvijek"
+ "Odbij i više ne pitaj"
+ "Onemogućeno: %1$d"
+ "sve onemogućeno"
+ "ništa nije onemogućeno"
+ "Dopusti"
+ "Aplikacije"
+ "Dopuštenja aplikacije"
+ "Više me ne pitaj"
+ "Nema dopuštenja"
+ "Dodatna dopuštenja"
+ "Otvori informacije o aplikaciji"
+
+ Još %1$d
+ Još %1$d
+ Još %1$d
+
+ "Ova je aplikacija napravljena za stariju verziju Androida. Ako ne dobije dopuštenje, možda više neće funkcionirati kako treba."
+ "izvršiti nepoznatu radnju"
+ "Aplikacije s dopuštenjem: %1$d od %2$d"
+ "Prikaži sustav"
+ "Sakrij sustav"
+ "Nema aplikacija"
+ "Postavke lokacije"
+ "%1$s pruža usluge lokacije za ovaj uređaj. Pristup lokaciji može se izmijeniti u postavkama lokacije."
+ "Ako ne odobrite ovo dopuštenje, osnovne značajke vašeg uređaja možda više neće funkcionirati pravilno."
+ "Provoditi se na temelju pravila"
+ "Pristup u pozadini onemogućen je pravilima"
+ "Pristup u pozadini omogućen je pravilima"
+ "Pristup u prednjem planu omogućen je pravilima"
+ "Kontrolira administrator"
+
+
+
+ "Uvijek"
+ "Samo dok se aplikacija koristi"
+ "Nikad"
+ "Učitavanje…"
+ "Sva dopuštenja"
+ "Ostale mogućnosti aplikacije"
+ "Zahtijevanje dopuštenja"
+ "Otkriveno je preklapanje na zaslonu"
+ "Da biste promijenili tu postavku dopuštenja, prvo morate isključiti preklapanje na zaslonu u Postavkama > Aplikacije"
+ "Otvori postavke"
+ "Android Wear"
+ "Radnje instaliranja i deinstaliranja nisu podržane na Wearu."
+ "Odaberite čemu će <b>%1$s</b> moći pristupiti"
+ "Aplikacija <b>%1$s</b> ažurirana je. Odaberite čemu će moći pristupiti."
+ "Otkaži"
+ "Nastavi"
+ "Nova dopuštenja"
+ "Trenutačna dopuštenja"
+ "Postavljanje aplikacije…"
+ "Nepoznato"
+ "Iz sigurnosnih razloga tablet nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Iz sigurnosnih razloga televizor nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Iz sigurnosnih razloga telefon nema dopuštenje za instaliranje nepoznatih aplikacija iz ovog izvora."
+ "Vaš telefon i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje telefona ili gubitak podataka do kojih može doći uslijed njezine upotrebe."
+ "Vaš tablet i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje tableta ili gubitak podataka do kojih može doći uslijed njezine upotrebe."
+ "Vaš TV i osobni podaci podložniji su napadima nepoznatih aplikacija. Instaliranjem te aplikacije prihvaćate odgovornost za oštećenje televizora ili gubitak podataka do kojih može doći uslijed njezine upotrebe."
+ "Nastavi"
+ "Postavke"
+ "Instaliranje/deinstaliranje Wear apl."
+
diff --git a/packages/PackageInstaller/res/values-hu-television/strings.xml b/packages/PackageInstaller/res/values-hu-television/strings.xml
new file mode 100644
index 0000000000000..0557700a7fc15
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Megtagadás, és ne jelenjen meg többé"
+ "Ezt később módosíthatja a Beállítások > Alkalmazások pontnál"
+ "%1$s/%2$s"
+ "Rendszeralkalmazások megjelenítése"
+ "Alkalmazásengedélyek"
+ "Alkalmazásengedélyek"
+ "%1$s – jogosultságok"
+ "További engedélyek"
+ "%1$s – jogosultságok"
+
diff --git a/packages/PackageInstaller/res/values-hu-watch/strings.xml b/packages/PackageInstaller/res/values-hu-watch/strings.xml
new file mode 100644
index 0000000000000..8ae3504e9a709
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Megtagadás, ne jelenjen meg"
+ "%2$s/%1$s"
+ "Rendszeralkalmazások megjelenítése"
+ "Nem változtatható"
+ "Igen"
+ "Mégse"
+
diff --git a/packages/PackageInstaller/res/values-hu/strings.xml b/packages/PackageInstaller/res/values-hu/strings.xml
new file mode 100644
index 0000000000000..5e965508890d0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hu/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Csomagtelepítő"
+ "Tovább"
+ "Telepítés"
+ "Kész"
+ "Mégse"
+ "Telepítés..."
+ "%1$s telepítése…"
+ "Alkalmazás telepítve."
+ "Telepíti ezt az alkalmazást? Az a következőkhöz fog hozzáférést kapni:"
+ "Telepíti ezt az alkalmazást? Az alkalmazás nem igényel különleges hozzáférést."
+ "Telepít egy frissítést ehhez a meglévő alkalmazáshoz? A meglévő adatai nem vesznek el. A frissített alkalmazás a következőkhöz kap hozzáférést:"
+ "Telepít egy frissítést ehhez a beépített alkalmazáshoz? A meglévő adatai nem vesznek el. A frissített alkalmazás a következőkhöz kap hozzáférést:"
+ "Telepít egy frissítést ehhez a meglévő alkalmazáshoz? A meglévő adatai nem vesznek el. A frissítés nem igényel különleges hozzáférést."
+ "Telepít egy frissítést ehhez a beépített alkalmazáshoz? A meglévő adatai nem vesznek el. A frissítés nem igényel különleges hozzáférést."
+ "Az alkalmazás nincs telepítve."
+ "A csomag telepítését letiltotta a rendszer."
+ "A nem csomagként telepített alkalmazás ütközik egy már létező csomaggal."
+ "A nem alkalmazásként telepített alkalmazás nem kompatibilis az Ön táblagépével."
+ "Ez az alkalmazás nem kompatibilis tévéjével."
+ "A nem alkalmazásként telepített alkalmazás nem kompatibilis az Ön telefonjával."
+ "A nem csomagként telepített alkalmazás érvénytelen."
+ "A(z) %1$s alkalmazás nem telepíthető táblagépére."
+ "A(z) %1$s alkalmazás nem telepíthető a tévéjére."
+ "A(z) %1$s alkalmazás nem telepíthető telefonjára."
+ "Megnyitás"
+ "A rendszergazda nem engedélyezi az ismeretlen forrásokból származó alkalmazások telepítését"
+ "Ez a felhasználó nem telepíthet ismeretlen alkalmazásokat"
+ "Ez a felhasználó nem telepíthet alkalmazásokat"
+ "OK"
+ "Alkalmazások kezelése"
+ "Nincs elég hely"
+ "A(z) %1$s alkalmazást nem lehet telepíteni. Szabadítson fel egy kis helyet, és próbálja újra."
+ "Az alkalmazás nem található"
+ "Az alkalmazás nem található a telepített alkalmazások listájában."
+ "Nem engedélyezett"
+ "A jelenlegi felhasználó számára nem engedélyezett az eltávolítás végrehajtása."
+ "Hiba"
+ "Az alkalmazás nem távolítható el."
+ "Alkalmazás eltávolítása"
+ "Frissítés eltávolítása"
+ "A(z) %1$s a következő alkalmazás része:"
+ "Eltávolítja ezt az alkalmazást?"
+ "Szeretné eltávolítani ezt az alkalmazást ""minden"" felhasználónál? Az alkalmazást és adatait az eszköz ""minden"" felhasználójánál töröljük."
+ "Eltávolítja ezt az alkalmazást %1$s felhasználó esetében?"
+ "Lecseréli az alkalmazást a gyári verzióra? Minden adat törlődik."
+ "Lecseréli az alkalmazást a gyári verzióra? Minden adat törlődik. Ez az eszköz összes felhasználóját érinti, így a munkaprofilokkal rendelkezőket is."
+ "Futó telepítések"
+ "Sikertelen telepítések"
+ "Eltávolítás..."
+ "A(z) %1$s eltávolítása folyamatban van…"
+ "Az eltávolítás befejeződött."
+ "A(z) %1$s eltávolítása befejeződött"
+ "Az eltávolítás sikertelen."
+ "A(z) %1$s eltávolítása nem sikerült."
+ "Nem lehet eltávolítani az aktív eszközrendszergazdai alkalmazást"
+ "Nem lehet eltávolítani az aktív eszközrendszergazdai alkalmazást %1$s felhasználó esetében"
+ "Egyes felhasználóknak/profiloknak szüksége van erre, másoknál pedig eltávolították"
+ "Ez az alkalmazás szükséges a profiljához, így nem távolítható el."
+ "Az alkalmazásra szüksége van az eszköz adminisztrátorának, és nem távolítható el."
+ "Eszközrendszergazdai alkalmazások kezelése"
+ "Felhasználók kezelése"
+ "Nem sikerült a(z) %1$s eltávolítása"
+ "Gond volt a csomag elemzésekor."
+ "Új"
+ "Mind"
+ "Adatvédelem"
+ "Eszközhozzáférés"
+ "A frissítés nem igényel új engedélyeket."
+ "Elutasítás"
+ "További információ"
+ "Tiltás mindenképpen"
+ "%2$s/%1$s"
+ "Engedélyezi a(z) <b>%1$s</b> számára a következőt: %2$s?"
+ "Mindig engedélyezi a(z) <b>%1$s</b> számára a következőt: %2$s?"
+ "Az alkalmazás használatakor"
+ "Mindig"
+ "Megtagadás, és ne jelenjen meg többé"
+ "%1$d van letiltva"
+ "az összes le van tiltva"
+ "egy sincs letiltva"
+ "Engedélyezés"
+ "Alkalmazások"
+ "Alkalmazásengedélyek"
+ "Ne jelenjen meg többé"
+ "Nincs engedély"
+ "További engedélyek"
+ "Alkalmazásinformációk megnyitása"
+
+ %1$d további
+ %1$d további
+
+ "Ez az alkalmazás az Android egy korábbi verziójához készült. Az engedély megtagadása esetén előfordulhat, hogy a továbbiakban nem fog megfelelően működni."
+ "végrehajt egy ismeretlen műveletet"
+ "%2$d/%1$d alkalmazás engedélyezve"
+ "Rendszerfolyamatok megjelenítése"
+ "Rendszerfolyamatok elrejtése"
+ "Nincsenek alkalmazások"
+ "Helybeállítások"
+ "A(z) %1$s helyszolgáltatásokat biztosít ennek az eszköznek. A helyhozzáférést a helybeállításokban lehet módosítani."
+ "Ha ezt nem engedélyezi, akkor előfordulhat, hogy az eszköz egyes alapfunkciói nem megfelelően fognak működni."
+ "Irányelv által kényszerítve"
+ "Háttérhozzáférés házirend által letiltva"
+ "Háttérhozzáférés házirend által engedélyezve"
+ "Előtérbeli hozzáférés házirend által engedélyezve"
+ "Rendszergazda által irányítva"
+
+
+
+ "Mindig"
+ "Az alkalmazás használatakor"
+ "Soha"
+ "Betöltés…"
+ "Az összes engedély"
+ "Egyéb alkalmazáslehetőségek"
+ "Engedélykérés"
+ "Képernyőfedvény észlelve"
+ "Ennek az engedélynek a módosításához először ki kell kapcsolnia a képernyőfedvényt a Beállítások > Alkalmazások menüben"
+ "Beállítások megnyitása"
+ "Android Wear"
+ "A Wear nem támogatja a telepítés/eltávolítás műveletet."
+ "Válassza ki, hogy a(z) <b>%1$s</b> mihez férjen hozzá"
+ "A(z) <b>%1$s</b> frissítése megtörtént. Válassza ki, hogy mihez férjen hozzá ez az alkalmazás."
+ "Mégse"
+ "Tovább"
+ "Új engedélyek"
+ "Jelenlegi engedélyek"
+ "Alkalmazás fokozatos közzététele…"
+ "Ismeretlen"
+ "Az Ön biztonsága érdekében táblagépe nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."
+ "Az Ön biztonsága érdekében tévéje nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."
+ "Az Ön biztonsága érdekében telefonja nem telepíthet ebből a forrásból származó ismeretlen alkalmazásokat."
+ "Telefonja és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a telefont ért károkért."
+ "Táblagépe és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a táblagépet ért károkért."
+ "Tévéje és személyes adatai fokozott kockázatnak vannak kitéve az ismeretlen alkalmazások támadásaival szemben. Az alkalmazás telepítésével elismeri, hogy Ön a felelős az alkalmazás használatából eredő esetleges adatvesztésért és a tévét ért károkért."
+ "Tovább"
+ "Beállítások"
+ "Wear-alkalmazások telepítése/törlése"
+
diff --git a/packages/PackageInstaller/res/values-hy-television/strings.xml b/packages/PackageInstaller/res/values-hy-television/strings.xml
new file mode 100644
index 0000000000000..f2606734c17ea
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Մերժել և այլևս չհարցնել"
+ "Կարող եք փոխել սա ավելի ուշ Կարգավորումներում և Հավելվածներում"
+ "%1$s / %2$s"
+ "Ցուցադրել համակարգի հավելվածները"
+ "Հավելվածի թույլտվությունները"
+ "Հավելվածի թույլտվությունները"
+ "%1$s թույլտվությունները"
+ "Լրացուցիչ թույլտվություններ"
+ "%1$s թույլտվությունները"
+
diff --git a/packages/PackageInstaller/res/values-hy-watch/strings.xml b/packages/PackageInstaller/res/values-hy-watch/strings.xml
new file mode 100644
index 0000000000000..5538858c65812
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Մերժել, այլևս չհարցնել"
+ "%1$s / %2$s"
+ "Ցուցադրել համակարգի հավելվածները"
+ "Հնարավոր չէ փոխել"
+ "Այո"
+ "Չեղարկել"
+
diff --git a/packages/PackageInstaller/res/values-hy/strings.xml b/packages/PackageInstaller/res/values-hy/strings.xml
new file mode 100644
index 0000000000000..e89f1ec0f4da8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-hy/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Փաթեթի տեղադրիչ"
+ "Հաջորդը"
+ "Տեղադրել"
+ "Պատրաստ է"
+ "Չեղարկել"
+ "Տեղադրվում է..."
+ "%1$s-ի տեղադրում…"
+ "Հավելվածը տեղադրված է:"
+ "Ցանկանու՞մ եք տեղադրել այս ծրագիրը: Այն մուտքի հնարավորություն կունենա`"
+ "Ցանկանու՞մ եք տեղադրել այս հավելվածը: Այն հատուկ մուտք չի պահանջում:"
+ "Ցանկանու՞մ եք այս առկա հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Նորացված հավելվածը կստանա մատչում`"
+ "Ցանկանու՞մ եք այս ներկառուցված հավելվածում թարմացում տեղադրել: Ձեր առկա տվյալները չեն կորի: Նորացված հավելվածը կստանա մատչում `"
+ "Ցանկանու՞մ եք այս առկա հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Այն չի պահանջում որևէ հատուկ մուտք:"
+ "Ցանկանու՞մ եք այս ներկառուցված հավելվածում թարմացում տեղադրել: Ձեր ընթացիկ տվյալները չեն կորի: Այն չի պահանջում որևէ հատուկ մուտք:"
+ "Հավելվածը տեղադրված չէ:"
+ "Փաթեթի տեղադրումն արգելափակվել է:"
+ "Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթն ունի հակասություն առկա փաթեթի հետ:"
+ "Հավելվածը չի տեղադրվել, քանի որ այն համատեղելի չէ ձեր պլանշետի հետ:"
+ "Այս հավելվածը համատեղելի չէ ձեր հեռուստացույցի հետ:"
+ "Հավելվածը չի տեղադրվել, քանի որ այն համատեղելի չէ ձեր հեռախոսի հետ:"
+ "Հավելվածը չի տեղադրվել, քանի որ տեղադրման փաթեթը, կարծես թե, վնասված է:"
+ "%1$s-ը չհաջողվեց տեղադրել ձեր պլանշետում:"
+ "Չհաջողվեց տեղադրել %1$s հավելվածը ձեր հեռուստացույցի վրա:"
+ "%1$s-ը չհաջողվեց տեղադրել ձեր հեռախոսում:"
+ "Բացել"
+ "Ձեր ադմինիստրատորը թույլ չի տալիս տեղադրել հավելվածներ անհայտ աղբյուրներից"
+ "Այս օգտատերը չի կարող անհայտ հավելվածներ տեղադրել"
+ "Այս օգտատիրոջը չի թույլատրվում տեղադրել հավելվածներ"
+ "Հաստատել"
+ "Կառավարել հավելվածները"
+ "Տարածքից դուրս"
+ "%1$s-ը չհաջողվեց տեղադրել: Ազատեք որոշակի տարածք և կրկին փորձեք:"
+ "Հավելվածը չի գտնվել"
+ "Հավելվածը չի գտնվել տեղադրված հավելվածների ցանկում:"
+ "Արգելված է"
+ "Ընթացիկ օգտատերը հեռացնելու թույլտվություն չունի:"
+ "Սխալ"
+ "Հնարավոր չէ հեռացնել հավելվածը:"
+ "Ապատեղադրել հավելվածը"
+ "Ապատեղադրել թարմացումը"
+ "%1$s-ը հետևյալ հավելվածի մասն է`"
+ "Ուզո՞ւմ եք ապատեղադրել այս հավելվածը։"
+ "Ցանկանու՞մ եք ապատեղադրել այս հավելվածը ""բոլոր"" օգտատերերի համար: Հավելվածը և դրա տվյալները կհեռացվեն սարքի ""բոլոր"" օգտատերերից:"
+ "Ցանկանում եք ապատեղադրե՞լ այս ծրագիրը %1$s օգտատիրոջ համար:"
+ "Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն:"
+ "Փոխարինե՞լ այս հավելվածը գործարանային տարբերակով: Բոլոր տվյալները կհեռացվեն: Դա վերաբերում է այս սարքի բոլոր օգտատերերին, այդ թվում նաև աշխատանքային պրոֆիլներ ունեցողներին:"
+ "Ընթացիկ հեռացումներ"
+ "Ձախողված հեռացումներ"
+ "Ապատեղադրում է..."
+ "%1$s հավելվածը հեռացվում է…"
+ "Ապատեղադրումը ավարտված է:"
+ "Հեռացված %1$s"
+ "Ապատեղադրումն անհաջող է:"
+ "Չհաջողվեց հեռացնել %1$s հավելվածը:"
+ "Հնարավոր չէ հեռացնել ակտիվ սարքի ադմինիստրատորի հավելվածը"
+ "Հնարավոր չէ հեռացնել ակտիվ սարքի ադմինիստրատորի հավելվածը %1$s օգտատիրոջ համար"
+ "Այս հավելվածն անհրաժեշտ է որոշ օգտատերերի կամ պրոֆիլների համար և մնացածի մոտ հեռացվել է"
+ "Այս հավելվածն անհրաժեշտ է ձեր պրոֆիլի համար: Այն հնարավոր չէ հեռացնել:"
+ "Ծրագիրը ձեր սարքի ադմինիստրատորի կողմից նշվել է որպես պարտադիր և չի կարող հեռացվել:"
+ "Կառավարել սարքի ադմինիստրատորի հավելվածները"
+ "Կառավարել օգտատերերին"
+ "%1$s-ը չհաջողվեց ապատեղադրել:"
+ "Փաթեթը վերլուծելիս խնդիր առաջացավ:"
+ "Նոր"
+ "Բոլորը"
+ "Գաղտնիություն"
+ "Սարքի մատչում"
+ "Այս թարմացումը պահանջում է, որ նոր թույլտվություններ չտրվեն:"
+ "Մերժել"
+ "Այլ տեղեկություններ"
+ "Մերժել ամեն դեպքում"
+ "%1$s-ը %2$s-ից"
+ "Թույլատրե՞լ <b>%1$s</b> հավելվածին %2$s:"
+ "Միշտ թույլ տա՞լ <b>%1$s</b> հավելվածին %2$s:"
+ "Միայն հավելվածն օգտագործելիս"
+ "Միշտ"
+ "Մերժել և այլևս չհարցնել"
+ "կասեցվել է %1$d թույլտվություն"
+ "բոլոր թույլտվությունները կասեցվել են"
+ "ոչ մի թույլտվություն չի կասեցվել"
+ "Թույլատրել"
+ "Հավելվածներ"
+ "Հավելվածների թույլտվություններ"
+ "Այլևս չհարցնել"
+ "Թույլտվություններ չկան"
+ "Լրացուցիչ թույլտվություններ"
+ "Հավելվածի մասին"
+
+ Եվս %1$d
+ Եվս %1$d
+
+ "Այս հավելվածը նախատեսված է Android-ի ավելի հին տարբերակի համար: Եթե մերժեք թույլտվությունը, այն կարող է չաշխատել ինչպես հարկն է:"
+ "թույլատրել անհայտ գործողություն"
+ "Թույլատրված է %1$d հավելվածի՝ %2$d-ից"
+ "Ցուցադրել համակարգայինները"
+ "Թաքցնել համակարգայինները"
+ "Հավելվածներ չկան"
+ "Տեղորոշման կարգավորումներ"
+ "Այս սարքի տեղորոշման ծառայությունները տրամադրում է %1$s հավելվածը: Տեղադրության ցուցադրման կարգավորումները կարող եք փոխել տեղադրության կարգավորումներից:"
+ "Եթե չտրամադրեք այս թույլտվությունը, ձեր սարքի հիմնական գործառույթները հնարավոր է սխալ աշխատեն:"
+ "Սահմանված է կանոններով"
+ "Հասանելիությունը ֆոնային ռեժիմում անջատած է կանոնի համաձայն"
+ "Հասանելիությունը ֆոնային ռեժիմում միացված է կանոնի համաձայն"
+ "Հասանելիությունն ակտիվ ռեժիմում միացված է կանոնի համաձայն"
+ "Վերահսկվում է ադմինիստրատորի կողմից"
+
+
+
+ "Միշտ"
+ "Միայն հավելվածն օգտագործելիս"
+ "Երբեք"
+ "Բեռնում…"
+ "Բոլոր թույլտվությունները"
+ "Այլ հավելվածների հնարավորությունները"
+ "Թույլտվության հարցում"
+ "Ցուցադրում այլ պատուհանների վրա"
+ "Այս թույլտվության կարգավորումները փոխելու համար նախ անհրաժեշտ է անջատել էկրանի վերադրումը՝ անցնելով Կարգավորումներ > Հավելվածներ"
+ "Բացել կարգավորումները"
+ "Android Wear"
+ "Տեղադրման/հեռացման գործողությունները Android Wear-ում չեն աջակցվում:"
+ "Ընտրեք, ինչ թույլտվություններ եք ցանկանում տրամադրել <b>%1$s</b> հավելվածին"
+ "<b>%1$s</b> հավելվածը թարմացվել է: Ընտրեք, ինչ թույլտվություններ եք ցանկանում տրամադրել այդ հավելվածին:"
+ "Չեղարկել"
+ "Շարունակել"
+ "Նոր թույլտվությունները"
+ "Առկա թույլտվությունները"
+ "Հավելվածի նախապատրաստում…"
+ "Անհայտ"
+ "Անվտանգության նկատառումներից ելնելով՝ ձեր պլանշետին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"
+ "Անվտանգության նկատառումներից ելնելով՝ ձեր հեռուստացույցին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"
+ "Անվտանգության նկատառումներից ելնելով՝ ձեր հեռախոսին չի թույլատրվում այս աղբյուրից տեղադրել անհայտ հավելվածներ:"
+ "Ձեր հեռախոսը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր հեռախոսին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"
+ "Ձեր պլանշետը և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր պլանշետին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"
+ "Ձեր TV-ն և անձնական տվյալներն առավել խոցելի են անհայտ հավելվածների գրոհների նկատմամբ: Տեղադրելով այս հավելվածը՝ դուք ընդունում եք, որ պատասխանատվություն եք կրում հավելվածի օգտագործման հետևանքով ձեր TV-ին պատճառած ցանկացած վնասի կամ տվյալների կորստի համար:"
+ "Շարունակել"
+ "Կարգավորումներ"
+ "Wear հավելվածների տեղադրում/հեռացում"
+
diff --git a/packages/PackageInstaller/res/values-in-television/strings.xml b/packages/PackageInstaller/res/values-in-television/strings.xml
new file mode 100644
index 0000000000000..8e6e9ab25b510
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Tolak dan jangan tanya lagi"
+ "Anda dapat mengubah ini nanti di Setelan > Aplikasi"
+ "%1$s / %2$s"
+ "Tampilkan aplikasi sistem"
+ "Izin aplikasi"
+ "Izin aplikasi"
+ "Izin %1$s"
+ "Izin tambahan"
+ "Izin %1$s"
+
diff --git a/packages/PackageInstaller/res/values-in-watch/strings.xml b/packages/PackageInstaller/res/values-in-watch/strings.xml
new file mode 100644
index 0000000000000..ca2e087ee5d28
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Tolak, jangan tanya lagi"
+ "%1$s / %2$s"
+ "Tampilkan aplikasi sistem"
+ "Tidak dapat diubah"
+ "Ya"
+ "Batal"
+
diff --git a/packages/PackageInstaller/res/values-in/strings.xml b/packages/PackageInstaller/res/values-in/strings.xml
new file mode 100644
index 0000000000000..afae29a84bb49
--- /dev/null
+++ b/packages/PackageInstaller/res/values-in/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pemasang paket"
+ "Berikutnya"
+ "Instal"
+ "Selesai"
+ "Batal"
+ "Memasang..."
+ "Menginstal %1$s…"
+ "Apl terpasang."
+ "Apakah Anda ingin memasang aplikasi ini? Aplikasi akan memiliki akses ke:"
+ "Apakah Anda ingin memasang aplikasi ini? Aplikasi tidak memerlukan akses khusus apa pun."
+ "Apakah Anda ingin memasang pembaruan ke aplikasi yang ada? Data Anda yang ada tidak akan hilang. Aplikasi yang diperbarui akan mendapatkan akses ke:"
+ "Apakah Anda ingin memasang pembaruan ke aplikasi yang tertanam? Data Anda yang ada tidak akan hilang. Aplikasi yang diperbarui akan mendapatkan akses ke:"
+ "Anda ingin memasang pembaruan ke aplikasi yang ada ini? Data Anda yang ada akan hilang. Tindakan ini tidak memerlukan akses khusus apa pun."
+ "Anda ingin memasang pembaruan ke aplikasi yang ada di dalamnya? Data Anda yang ada akan hilang. Tindakan ini tidak memerlukan akses khusus apa pun."
+ "Apl tidak terpasang."
+ "Paket diblokir sehingga tidak dapat dipasang."
+ "Aplikasi yang tidak dipasang sebagai paket akan bentrok dengan paket yang sudah ada."
+ "Aplikasi yang tidak dipasang sebagai aplikasi tidak kompatibel dengan tablet Anda."
+ "Aplikasi ini tidak kompatibel dengan TV Anda."
+ "Aplikasi yang tidak dipasang sebagai aplikasi tidak kompatibel dengan ponsel Anda."
+ "Aplikasi yang tidak dipasang sebagai paket tampaknya tidak valid."
+ "%1$s tidak dapat dipasang pada tablet Anda."
+ "%1$s tidak dapat dipasang di TV."
+ "%1$s tidak dapat dipasang pada ponsel Anda."
+ "Buka"
+ "Admin tidak mengizinkan penginstalan aplikasi yang didapatkan dari sumber tidak dikenal"
+ "Aplikasi yang tidak dikenal tidak dapat diinstal oleh pengguna ini"
+ "Pengguna ini tidak diizinkan menginstal aplikasi"
+ "Oke"
+ "Kelola apl"
+ "Kehabisan ruang penyimpanan"
+ "%1$s tidak dapat dipasang. Kosongkan sebagian ruang dan coba lagi."
+ "Apl tidak ditemukan"
+ "Apl tersebut tidak ditemukan di dalam daftar apl yang terpasang."
+ "Tidak diizinkan"
+ "Pengguna saat ini tidak diizinkan meng-uninstal."
+ "Kesalahan"
+ "Aplikasi tidak dapat dipasang."
+ "Uninstal apl"
+ "Uninstal pembaruan"
+ "%1$s adalah bagian dari apl berikut:"
+ "Apakah Anda ingin meng-uninstal apl ini?"
+ "Anda ingin mencopot aplikasi ini untuk ""semua"" pengguna? Aplikasi dan datanya akan dihapus dari ""semua"" pengguna pada perangkat."
+ "Ingin meng-uninstal aplikasi ini untuk pengguna %1$s?"
+ "Gantikan aplikasi ini dengan versi pabrik? Semua data akan dihapus."
+ "Gantikan aplikasi ini dengan versi pabrik? Semua data akan dihapus. Tindakan ini memengaruhi semua pengguna perangkat ini, termasuk yang memiliki profil kerja."
+ "Menjalankan uninstal"
+ "Uninstal yang gagal"
+ "Mencopot pemasangan..."
+ "Mencopot pemasangan %1$s…"
+ "Pencopotan pemasangan selesai."
+ "%1$s di-uninstal"
+ "Pencopotan pemasangan tidak berhasil."
+ "Gagal meng-uninstal %1$s."
+ "Tidak dapat meng-uninstal aplikasi admin perangkat yang aktif"
+ "Tidak dapat meng-uninstal aplikasi admin perangkat yang aktif untuk %1$s"
+ "Aplikasi ini diperlukan untuk beberapa pengguna atau profil, dan telah dicopot pemasangannya untuk yang lainnya"
+ "Aplikasi ini diperlukan untuk profil Anda dan tidak dapat dicopot pemasangannya."
+ "Aplikasi diwajibkan administrator perangkat & pemasangannya tidak bisa dicopot."
+ "Kelola aplikasi admin perangkat"
+ "Kelola pengguna"
+ "%1$s tidak dapat dicopot pemasangannya."
+ "Terjadi masalah saat mengurai paket."
+ "Baru"
+ "Semua"
+ "Privasi"
+ "Akses Perangkat"
+ "Pembaruan ini tidak memerlukan izin baru."
+ "Tolak"
+ "Info selengkapnya"
+ "Tetap tolak"
+ "%1$s dari %2$s"
+ "Izinkan <b>%1$s</b> untuk %2$s?"
+ "Selalu izinkan <b>%1$s</b> untuk %2$s?"
+ "Hanya saat menggunakan aplikasi"
+ "Selalu"
+ "Tolak dan jangan tanya lagi"
+ "%1$d dinonaktifkan"
+ "semua dinonaktifkan"
+ "tidak ada yang dinonaktifkan"
+ "Izinkan"
+ "Aplikasi"
+ "Izin aplikasi"
+ "Jangan tanya lagi"
+ "Tidak ada izin"
+ "Izin tambahan"
+ "Buka info aplikasi"
+
+ %1$d lainnya
+ %1$d lainnya
+
+ "Aplikasi ini dirancang untuk versi lama Android. Menolak izin dapat menyebabkan aplikasi tidak berfungsi lagi sesuai harapan."
+ "melakukan tindakan yang tidak dikenal"
+ "%1$d dari %2$d aplikasi diizinkan"
+ "Tampilkan sistem"
+ "Sembunyikan sistem"
+ "Tidak ada aplikasi"
+ "Setelan Lokasi"
+ "%1$s adalah penyedia layanan lokasi untuk perangkat ini. Akses lokasi dapat diubah dari setelan lokasi."
+ "Jika Anda menolak izin ini, fitur dasar perangkat mungkin tidak berfungsi lagi sesuai harapan."
+ "Diterapkan menurut kebijakan"
+ "Akses background dinonaktifkan oleh kebijakan"
+ "Akses background diaktifkan oleh kebijakan"
+ "Akses latar depan diaktifkan oleh kebijakan"
+ "Dikontrol oleh admin"
+
+
+
+ "Selalu"
+ "Hanya saat menggunakan aplikasi"
+ "Tidak pernah"
+ "Memuat…"
+ "Semua izin"
+ "Kemampuan aplikasi lainnya"
+ "Permintaan izin"
+ "Hamparan layar terdeteksi"
+ "Untuk mengubah setelan izin ini, terlebih dahulu Anda harus menonaktifkan hamparan layar dari Setelan > Aplikasi"
+ "Buka setelan"
+ "Android Wear"
+ "Instal/Uninstal tidak didukung di Wear."
+ "Pilih item yang boleh diakses oleh <b>%1$s</b>"
+ "<b>%1$s</b> telah diperbarui. Pilih item yang boleh diakses oleh aplikasi ini."
+ "Batal"
+ "Lanjutkan"
+ "Izin baru"
+ "Izin saat ini"
+ "Menyiapkan aplikasi..."
+ "Tidak dikenal"
+ "Demi keamanan, TV tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."
+ "Demi keamanan, TV tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."
+ "Demi keamanan, ponsel tidak diizinkan menginstal aplikasi yang tidak dikenal dari sumber ini."
+ "Data pribadi dan ponsel lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan ponsel atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."
+ "Data pribadi dan tablet lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan tablet atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."
+ "Data pribadi dan TV lebih rentan terhadap serangan oleh aplikasi yang tidak dikenal. Dengan menginstal aplikasi ini, Anda setuju bahwa Anda bertanggung jawab atas kerusakan TV atau kehilangan data yang mungkin diakibatkan oleh penggunaannya."
+ "Lanjutkan"
+ "Setelan"
+ "Melakukan instal/uninstal aplikasi Wear"
+
diff --git a/packages/PackageInstaller/res/values-is-television/strings.xml b/packages/PackageInstaller/res/values-is-television/strings.xml
new file mode 100644
index 0000000000000..0c1fad7c108b5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Hafna og ekki spyrja aftur"
+ "Þú getur breytt þessu seinna undir Stillingar og forrit"
+ "%1$s / %2$s"
+ "Sýna kerfisforrit"
+ "Heimildir forrita"
+ "Heimildir forrita"
+ "Heimildir fyrir %1$s"
+ "Viðbótarheimildir"
+ "Heimildir fyrir %1$s"
+
diff --git a/packages/PackageInstaller/res/values-is-watch/strings.xml b/packages/PackageInstaller/res/values-is-watch/strings.xml
new file mode 100644
index 0000000000000..b82da5e5ff5f1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Hafna, ekki spyrja aftur"
+ "%1$s / %2$s"
+ "Sýna kerfisforrit"
+ "Má ekki breyta"
+ "Já"
+ "Hætta við"
+
diff --git a/packages/PackageInstaller/res/values-is/strings.xml b/packages/PackageInstaller/res/values-is/strings.xml
new file mode 100644
index 0000000000000..ea0bdcb3eea1e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-is/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Uppsetningarforrit pakka"
+ "Áfram"
+ "Setja upp"
+ "Lokið"
+ "Hætta við"
+ "Setur upp…"
+ "Setur upp %1$s…"
+ "Forritið er uppsett."
+ "Viltu setja þetta forrit upp? Það mun fá aðgang að:"
+ "Viltu setja þetta forrit upp? Það krefst ekki neins sérstaks aðgangs."
+ "Viltu setja upp uppfærslu á þessu uppsetta forriti? Eldri gögn glatast ekki. Uppfærða forritið mun fá aðgang að:"
+ "Viltu setja upp uppfærslu á þessu innbyggða forriti? Eldri gögn glatast ekki. Uppfærða forritið mun fá aðgang að:"
+ "Viltu setja upp uppfærslu á þessu uppsetta forriti? Eldri gögn glatast ekki. Forritið krefst ekki neins sérstaks aðgangs."
+ "Viltu setja upp uppfærslu á þessu innbyggða forriti? Eldri gögn glatast ekki. Forritið krefst ekki neins sérstaks aðgangs."
+ "Forritið er ekki uppsett."
+ "Lokað var á uppsetningu pakkans."
+ "Forritið var ekki sett upp vegna árekstra á milli pakkans og annars pakka."
+ "Forritið var ekki sett upp því að forritið er ekki samhæft við spjaldtölvuna þína."
+ "Þetta forrit er ekki samhæft við sjónvarpið."
+ "Forritið var ekki sett upp því að forritið er ekki samhæft við símann þinn."
+ "Forritið var ekki sett upp því að pakkinn virðist vera ógildur."
+ "Ekki tókst að setja %1$s upp í spjaldtölvunni."
+ "Ekki var hægt að setja %1$s upp í sjónvarpinu."
+ "Ekki tókst að setja %1$s upp í símanum."
+ "Opna"
+ "Kerfisstjórinn leyfir ekki uppsetningu forrita af óþekktum uppruna"
+ "Þessi notandi getur ekki sett upp óþekkt forrit"
+ "Þessi notandi hefur ekki leyfi til að setja upp forrit"
+ "Í lagi"
+ "Stjórna forritum"
+ "Ekkert pláss eftir"
+ "Ekki tókst að setja upp %1$s. Losaðu um pláss og reyndu aftur."
+ "Forritið finnst ekki"
+ "Forritið fannst ekki á listanum yfir uppsett forrit."
+ "Ekki heimilað"
+ "Núverandi notandi hefur ekki heimild til að fjarlægja þetta."
+ "Villa"
+ "Ekki tókst að fjarlægja forritið."
+ "Fjarlægja forrit"
+ "Fjarlægja uppfærslu"
+ "%1$s er hluti af þessu forriti:"
+ "Viltu fjarlægja þetta forrit?"
+ "Viltu fjarlægja þetta forrit hjá ""öllum"" notendum? Forritið og gögn þess verða fjarlægð hjá ""öllum"" notendum tækisins."
+ "Viltu fjarlægja þetta forrit fyrir notandann %1$s?"
+ "Viltu skipta þessu forriti út fyrir verksmiðjuútgáfuna? Öll gögn verða fjarlægð."
+ "Viltu skipta þessu forriti út fyrir verksmiðjuútgáfuna? Öll gögn verða fjarlægð. Þetta hefur áhrif á alla notendur tækisins, þar á meðal þá sem eru með vinnusnið."
+ "Fjarlægingar í gangi"
+ "Fjarlægingar sem mistókust"
+ "Fjarlægir…"
+ "Fjarlægir %1$s…"
+ "Forritið hefur verið fjarlægt."
+ "Fjarlægði %1$s"
+ "Ekki tókst að fjarlægja forritið."
+ "Ekki tókst að fjarlægja %1$s."
+ "Ekki er hægt að fjarlægja virkt forrit tækjastjóra"
+ "Ekki er hægt að fjarlægja virkt forrit tækjastjóra fyrir %1$s"
+ "Þessa forrits er krafist hjá sumum notendum eða sniðum en var fjarlægt hjá öðrum"
+ "Sniðið þitt krefst þessa forrits og ekki er hægt að fjarlægja það."
+ "Stjórnandi tækisins krefst þessa forrits og ekki er hægt að fjarlægja það."
+ "Stjórna forritum tækjastjóra"
+ "Stjórna notendum"
+ "Ekki tókst að fjarlægja %1$s."
+ "Vandamál kom upp við að vinna úr pakkanum."
+ "Nýjar"
+ "Allar"
+ "Persónuvernd"
+ "Tækisaðgangur"
+ "Þessi uppfærsla krefst engra nýrra heimilda."
+ "Hafna"
+ "Frekari upplýsingar"
+ "Hafna samt"
+ "%1$s af %2$s"
+ "Leyfa forritinu <b>%1$s</b> að %2$s?"
+ "Viltu alltaf veita <b>%1$s</b> leyfi til að %2$s?"
+ "Aðeins þegar forrit er í notkun"
+ "Alltaf"
+ "Hafna og ekki spyrja aftur"
+ "%1$d óvirkar"
+ "allar óvirkar"
+ "engin óvirk"
+ "Leyfa"
+ "Forrit"
+ "Heimildir forrits"
+ "Ekki spyrja aftur"
+ "Engar heimildir"
+ "Viðbótarheimildir"
+ "Opna upplýsingar um forrit"
+
+ %1$d í viðbót
+ %1$d í viðbót
+
+ "Þetta forrit var hannað fyrir eldri útgáfu af Android. Ef því er ekki veitt heimild er hugsanlegt að það virki ekki rétt."
+ "framkvæma óþekkta aðgerð"
+ "%1$d forrit af %2$d leyfð"
+ "Sýna kerfisforrit"
+ "Fela kerfisforrit"
+ "Engin forrit"
+ "Staðsetningarstillingar"
+ "%1$s býður upp á staðsetningarþjónustu fyrir þetta tæki. Hægt er að breyta aðgangi að staðsetningu í stillingum staðsetningar."
+ "Ef þú veitir ekki þessa heimild getur verið að grunneiginleikar tækisins virki ekki lengur sem skyldi."
+ "Stjórnað af reglu"
+ "Bakgrunnsaðgangur óvirkur vegna reglu"
+ "Bakgrunnsaðgangur virkur vegna reglu"
+ "Forgrunnsaðgangur virkur vegna reglu"
+ "Stjórnað af kerfisstjóra"
+
+
+
+ "Alltaf"
+ "Aðeins þegar forrit er í notkun"
+ "Aldrei"
+ "Hleður…"
+ "Allar heimildir"
+ "Aðrir forritseiginleikar"
+ "Beiðni um heimild"
+ "Skjáyfirlögn greindist"
+ "Til að breyta þessari heimildarstillingu þarftu fyrst að slökkva á skjáyfirlögn undir Stillingar > Forrit"
+ "Opna stillingar"
+ "Android Wear"
+ "Aðgerðir til að setja upp / fjarlægja eru ekki studdar í Wear."
+ "Veldu hverju <b>%1$s</b> fær aðgang að"
+ "<b>%1$s</b> hefur verið uppfært. Veldu hverju forritið fær aðgang að."
+ "Hætta við"
+ "Halda áfram"
+ "Nýjar heimildir"
+ "Núgildandi heimildir"
+ "Setur upp forrit…"
+ "Óþekkt"
+ "Til að tryggja öryggi þitt er ekki heimild í spjaldtölvunni þinni fyrir uppsetningu óþekktra forrita frá þessari veitu."
+ "Til að tryggja öryggi þitt er ekki heimild í sjónvarpinu þínu fyrir uppsetningu óþekktra forrita frá þessari veitu."
+ "Til að tryggja öryggi þitt er ekki heimild í símanum þínum fyrir uppsetningu óþekktra forrita frá þessari veitu."
+ "Síminn þinn og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á símanum eða gagnatapi sem leiða kann af notkun þess."
+ "Spjaldtölvan þín og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á spjaldtölvunni eða gagnatapi sem leiða kann af notkun þess."
+ "Sjónvarpið þitt og persónuleg gögn eru berskjaldaðri fyrir árásum forrita af óþekktum uppruna. Með uppsetningu þessa forrits samþykkirðu að bera fulla ábyrgð á hverju því tjóni sem verða kann á sjónvarpinu eða gagnatapi sem leiða kann af notkun þess."
+ "Áfram"
+ "Stillingar"
+ "Uppsetning/fjarlæging Wear forrita"
+
diff --git a/packages/PackageInstaller/res/values-it-television/strings.xml b/packages/PackageInstaller/res/values-it-television/strings.xml
new file mode 100644
index 0000000000000..14c46de89690d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Nega e non chiedermelo più"
+ "Puoi modificare questa scelta in seguito in Impostazioni > App"
+ "%1$s/%2$s"
+ "Mostra app di sistema"
+ "Autorizzazioni app"
+ "Autorizzazioni app"
+ "Autorizzazioni %1$s"
+ "Altre autorizzazioni"
+ "Autorizzazioni %1$s"
+
diff --git a/packages/PackageInstaller/res/values-it-watch/strings.xml b/packages/PackageInstaller/res/values-it-watch/strings.xml
new file mode 100644
index 0000000000000..73b2ab15b1df4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Nega e non chiedermelo più"
+ "%1$s/%2$s"
+ "Mostra app di sistema"
+ "Imposs modif"
+ "Sì"
+ "Annulla"
+
diff --git a/packages/PackageInstaller/res/values-it/strings.xml b/packages/PackageInstaller/res/values-it/strings.xml
new file mode 100644
index 0000000000000..5870722726f31
--- /dev/null
+++ b/packages/PackageInstaller/res/values-it/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Installazione pacchetti"
+ "Avanti"
+ "Installa"
+ "Fine"
+ "Annulla"
+ "Installazione..."
+ "Installazione di %1$s…"
+ "App installata."
+ "Vuoi installare questa applicazione? Avrà accesso a:"
+ "Vuoi installare questa applicazione? Non richiede alcun accesso speciale."
+ "Vuoi installare un aggiornamento per questa applicazione esistente? I tuoi dati esistenti non andranno persi. L\'applicazione aggiornata avrà accesso a:"
+ "Vuoi installare un aggiornamento per questa applicazione integrata? I tuoi dati esistenti non andranno persi. L\'applicazione aggiornata avrà accesso a:"
+ "Vuoi installare un aggiornamento di questa applicazione esistente? I dati correnti verranno conservati. Non occorrono accessi speciali."
+ "Vuoi installare un aggiornamento di questa applicazione integrata? I dati correnti verranno conservati. Non occorrono accessi speciali."
+ "App non installata."
+ "È stata bloccata l\'installazione del pacchetto."
+ "App non installata poiché il pacchetto è in conflitto con un pacchetto esistente."
+ "App non installata poiché non compatibile con il tuo tablet."
+ "Questa app non è compatibile con la tua TV."
+ "App non installata poiché non compatibile con il tuo telefono."
+ "App non installata poiché il pacchetto risulta non valido."
+ "Impossibile installare %1$s sul tablet."
+ "Impossibile installare %1$s sulla tua TV."
+ "Impossibile installare %1$s sul telefono."
+ "Apri"
+ "L\'amministratore non consente l\'installazione di app ottenute da fonti sconosciute"
+ "Questo utente non può installare app sconosciute"
+ "L\'utente non è autorizzato a installare app"
+ "OK"
+ "Gestisci applicazioni"
+ "Spazio esaurito"
+ "Impossibile installare %1$s. Libera dello spazio e riprova."
+ "Applicazione non trovata"
+ "Impossibile trovare l\'applicazione nell\'elenco di applicazioni installate."
+ "Non autorizzato"
+ "L\'utente corrente non è autorizzato a eseguire questa disinstallazione."
+ "Errore"
+ "Impossibile disinstallare l\'app."
+ "Disinstalla applicazione"
+ "Disinstalla aggiornamento"
+ "%1$s fa parte della seguente applicazione:"
+ "Vuoi disinstallare questa applicazione?"
+ "Vuoi disinstallare questa applicazione per ""tutti"" gli utenti? L\'applicazione e i relativi dati verranno rimossi da ""tutti"" gli utenti configurati sul dispositivo."
+ "Disinstallare l\'app per l\'utente %1$s?"
+ "Sostituire questa app con la versione di fabbrica? Tutti i dati verranno rimossi."
+ "Sostituire questa app con la versione di fabbrica? Tutti i dati verranno rimossi. Saranno interessati tutti gli utenti del dispositivo, inclusi quelli che hanno profili di lavoro."
+ "Disinstallazioni attuali"
+ "Disinstallazioni non riuscite"
+ "Disinstallazione..."
+ "Disinstallazione di %1$s…"
+ "Disinstallazione completata."
+ "App %1$s disinstallata"
+ "Disinstallazione non riuscita."
+ "Impossibile disinstallare %1$s."
+ "Impossibile disinstallare l\'app di amministrazione del dispositivo attiva"
+ "Impossibile disinstallare l\'app di amministrazione del dispositivo attiva per %1$s"
+ "L\'app è necessaria per alcuni utenti/profili ed è stata disinstallata per altri"
+ "L\'app è necessaria per il tuo profilo e non può essere disinstallata."
+ "App richiesta dall\'amministratore del dispositivo. Non può essere disinstallata."
+ "Gestisci app di amministrazione del dispositivo"
+ "Gestisci utenti"
+ "Impossibile disinstallare %1$s."
+ "Errore durante l\'analisi del pacchetto."
+ "Nuove"
+ "Tutte"
+ "Privacy"
+ "Accesso dispositivo"
+ "Questo aggiornamento non richiede nuove autorizzazioni."
+ "Nega"
+ "Altre informazioni"
+ "Nega comunque"
+ "%1$s di %2$s"
+ "Consentire a <b>%1$s</b> di %2$s?"
+ "Vuoi consentire sempre all\'app <b>%1$s</b> di %2$s?"
+ "Solo durante l\'uso dell\'app"
+ "Sempre"
+ "Nega e non chiedermelo più"
+ "%1$d disattivate"
+ "tutte disattivate"
+ "nessuna disattivata"
+ "Consenti"
+ "App"
+ "Autorizzazioni app"
+ "Non chiedermelo più"
+ "Nessuna autorizzazione"
+ "Altre autorizzazioni"
+ "Visualizza informazioni sull\'app"
+
+ Altre %1$d
+ %1$d altra
+
+ "Questa app è stata sviluppata per una versione precedente di Android. Se l\'autorizzazione viene negata, l\'app potrebbe non funzionare più come previsto."
+ "esegue un\'azione sconosciuta"
+ "Sono consentite %1$d app su %2$d"
+ "Mostra app di sistema"
+ "Nascondi app di sistema"
+ "Nessuna app"
+ "Geolocalizzazione"
+ "%1$s è un fornitore di servizi di geolocalizzazione per questo dispositivo. È possibile modificare l\'accesso alla posizione dalle impostazioni sulla posizione."
+ "Se neghi questa autorizzazione, le funzionalità di base del dispositivo potrebbero non funzionare più come previsto."
+ "Applicata in base alle norme"
+ "Accesso in background disattivato in base alla norma"
+ "Accesso in background attivato in base alla norma"
+ "Accesso in primo piano attivato in base alla norma"
+ "Gestita dall\'amministratore"
+
+
+
+ "Sempre"
+ "Solo durante l\'uso dell\'app"
+ "Mai"
+ "Caricamento…"
+ "Tutte le autorizzazioni"
+ "Altre funzionalità dell\'app"
+ "Richiesta di autorizzazione"
+ "Overlay schermo rilevato"
+ "Per modificare questa impostazione di autorizzazione, devi innanzitutto disattivare l\'overlay schermo da Impostazioni > App"
+ "Apri impostazioni"
+ "Android Wear"
+ "Le azioni di installazione/disinstallazione non sono supportate su Wear."
+ "Scegli i dati a cui l\'app %1$s può accedere"
+ "L\'app <b>%1$s</b> è stata aggiornata. Scegli i dati a cui può accedere."
+ "Annulla"
+ "Continua"
+ "Nuove autorizzazioni"
+ "Autorizzazioni correnti"
+ "App in preparazione…"
+ "Sconosciuto"
+ "Per sicurezza, il tuo tablet non è autorizzato a installare app sconosciute da questa origine."
+ "Per sicurezza, la tua TV non è autorizzata a installare app sconosciute da questa origine."
+ "Per sicurezza, il tuo telefono non è autorizzato a installare app sconosciute da questa origine."
+ "I dati del telefono e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al telefono o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."
+ "I dati del tablet e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni al tablet o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."
+ "I dati della TV e i dati personali sono più vulnerabili agli attacchi di app sconosciute. Se installi questa app, accetti di essere responsabile degli eventuali danni alla TV o dell\'eventuale perdita di dati derivanti dall\'uso dell\'app."
+ "Continua"
+ "Impostazioni"
+ "Installazione/disinstallazione di app Wear"
+
diff --git a/packages/PackageInstaller/res/values-iw-television/strings.xml b/packages/PackageInstaller/res/values-iw-television/strings.xml
new file mode 100644
index 0000000000000..1ec1cd7b873b5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "דחה ואל תשאל שוב"
+ "ניתן לשנות זאת מאוחר יותר ב\'הגדרות\' > \'אפליקציות\'"
+ "%1$s / %2$s"
+ "הצג אפליקציות מערכת"
+ "הרשאות לאפליקציות"
+ "הרשאות לאפליקציות"
+ "הרשאות %1$s"
+ "הרשאות נוספות"
+ "הרשאות %1$s"
+
diff --git a/packages/PackageInstaller/res/values-iw-watch/strings.xml b/packages/PackageInstaller/res/values-iw-watch/strings.xml
new file mode 100644
index 0000000000000..34dd0d3cd2307
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "דחה ואל תשאל שוב"
+ "%1$s / %2$s"
+ "הצג אפליקציות מערכת"
+ "לא ניתן לשנות"
+ "כן"
+ "ביטול"
+
diff --git a/packages/PackageInstaller/res/values-iw/strings.xml b/packages/PackageInstaller/res/values-iw/strings.xml
new file mode 100644
index 0000000000000..414fe4c794027
--- /dev/null
+++ b/packages/PackageInstaller/res/values-iw/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "מתקין החבילה"
+ "הבא"
+ "התקן"
+ "סיום"
+ "ביטול"
+ "מתקין..."
+ "מתקין את %1$s…"
+ "האפליקציה הותקנה."
+ "האם ברצונך להתקין את האפליקציה? היא תקבל גישה אל:"
+ "האם ברצונך להתקין את האפליקציה? היא אינה דורשת גישה מיוחדת."
+ "האם ברצונך להתקין עדכון לאפליקציה קיימת זו? הנתונים הקיימים שלך לא יאבדו. האפליקציה המעודכנת תקבל גישה אל:"
+ "האם ברצונך להתקין עדכון לאפליקציה מובנית זו? הנתונים הקיימים שלך לא יאבדו. האפליקציה המעודכנת תקבל גישה אל:"
+ "האם ברצונך להתקין עדכון עבור אפליקציה קיימת זו? הנתונים הקיימים שלך לא יאבדו. אין צורך בגישה מיוחדת."
+ "האם ברצונך להתקין עדכון עבור אפליקציה מובנית זו? הנתונים הקיימים שלך לא יאבדו. אין צורך בגישה מיוחדת."
+ "האפליקציה לא הותקנה."
+ "החבילה נחסמה להתקנה."
+ "האפליקציה לא הותקנה כי החבילה מתנגשת עם חבילה קיימת."
+ "האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטאבלט."
+ "האפליקציה הזו אינה תואמת לטלוויזיה שלך."
+ "האפליקציה לא הותקנה כי האפליקציה אינה תואמת לטלפון."
+ "האפליקציה לא הותקנה כי נראה שהחבילה לא תקפה."
+ "לא ניתן להתקין את %1$s בטאבלט שלך."
+ "לא ניתן להתקין את %1$s בטלוויזיה שלך."
+ "לא ניתן להתקין את %1$s בטלפון שלך."
+ "פתח"
+ "מנהל המערכת שלך לא מתיר התקנה של אפליקציות ממקורות לא ידועים"
+ "למשתמש זה אין הרשאה להתקין אפליקציות שאינן מוכרות"
+ "למשתמש הזה אין הרשאה להתקין אפליקציות"
+ "אישור"
+ "נהל אפליקציות"
+ "אין מספיק שטח"
+ "לא ניתן להתקין את %1$s. פנה שטח ונסה שוב."
+ "האפליקציה לא נמצא"
+ "האפליקציה לא נמצאה ברשימת האפליקציות המותקנות."
+ "לא מורשה"
+ "המשתמש הנוכחי אינו מורשה להסיר את ההתקנה הזו."
+ "שגיאה"
+ "לא ניתן היה להסיר את התקנת האפליקציה."
+ "הסר את התקנת האפליקציה"
+ "הסר את התקנת העדכון"
+ "%1$s הוא חלק מהאפליקציה הבאה:"
+ "האם ברצונך להסיר את ההתקנה של אפליקציה זו?"
+ "האם אתה רוצה להסיר את האפליקציה הזו עבור ""כל"" המשתמשים? האפליקציה והנתונים שלה יוסרו מ""כל"" המשתמשים במכשיר."
+ "האם ברצונך להסיר את התקנתה של אפליקציה זו עבור המשתמש %1$s?"
+ "האם להחליף את האפליקציה הזאת בגירסת היצרן? כל הנתונים יוסרו."
+ "האם להחליף את האפליקציה הזאת בגירסת היצרן? כל הנתונים יוסרו. הפעולה תשפיע על כל משתמשי המכשיר, כולל משתמשים בעלי פרופיל עבודה."
+ "התקנות בתהליכי הסרה"
+ "הסרות התקנה שנכשלו"
+ "מסיר התקנה..."
+ "מסיר את ההתקנה של %1$s…"
+ "הסרת ההתקנה הסתיימה."
+ "ההתקנה של %1$s הוסרה"
+ "הסרת התקנה נכשלה."
+ "נכשלה הסרת ההתקנה של %1$s."
+ "לא ניתן להסיר את ההתקנה של אפליקציה פעילה של מנהל המכשיר"
+ "לא ניתן להסיר את ההתקנה של אפליקציה פעילה של מנהל המכשיר של %1$s"
+ "אפליקציה זו נדרשת לחלק מהמשתמשים או מהפרופילים והתקנתה הוסרה למשתמשים אחרים"
+ "האפליקציה הזו נחוצה לפרופיל שלך ולא ניתן להסיר את ההתקנה שלה."
+ "מנהל המכשיר שלך מחייב את קיומה של אפליקציה זו, ולא ניתן להסירה."
+ "אפליקציות למנהל המערכת של מכשיר מנוהל"
+ "ניהול משתמשים"
+ "לא ניתן להסיר את ההתקנה של %1$s."
+ "אירעה בעיה בניתוח החבילה."
+ "חדש"
+ "הכל"
+ "פרטיות"
+ "גישה למכשיר"
+ "עדכון זה לא דורש הרשאות חדשות."
+ "לא, תודה"
+ "למידע נוסף"
+ "לדחות בכל מקרה"
+ "%1$s מתוך %2$s"
+ "האם לאשר ל<b>%1$s</b> %2$s?"
+ "תמיד להרשות לאפליקציה <b>%1$s</b> %2$s?"
+ "רק בזמן השימוש באפליקציה"
+ "תמיד"
+ "יש לדחות ואין לשאול שוב"
+ "%1$d הרשאות מושבתות"
+ "כל ההרשאות מושבתות"
+ "אין הרשאות מושבתות"
+ "כן, זה בסדר"
+ "אפליקציות"
+ "הרשאות לאפליקציות"
+ "ראיתי פעם אחת, זה מספיק"
+ "אין הרשאות"
+ "הרשאות נוספות"
+ "פתיחה של פרטי האפליקציה"
+
+ עוד %1$d
+ עוד %1$d
+ עוד %1$d
+ עוד %1$d
+
+ "האפליקציה הזו עוצבה בשביל גרסה ישנה יותר של Android. דחיית ההרשאה עשויה לגרום לה לתפקד בצורה לקויה."
+ "ביצוע פעולה לא ידועה"
+ "%1$d מתוך %2$d אפליקציות קיבלו הרשאה"
+ "הרשאות המערכת"
+ "הסתר מערכת"
+ "אין אפליקציות"
+ "הגדרות מיקום"
+ "%1$s הוא ספק של שירותי מיקום בשביל המכשיר הזה. אפשר לשנות את גישת המיקום בהגדרות המיקום."
+ "אם תדחה את ההרשאה הזו, ייתכן שתכונות בסיסיות במכשיר לא יפעלו כצפוי."
+ "נאכף באמצעות מדיניות"
+ "הגישה ברקע מושבתת על ידי מדיניות"
+ "הגישה ברקע מופעלת על ידי מדיניות"
+ "הגישה בחזית מופעלת על ידי מדיניות"
+ "נמצא בשליטת מנהל מערכת"
+
+
+
+ "תמיד"
+ "רק בזמן השימוש באפליקציה"
+ "אף פעם"
+ "טוען..."
+ "כל ההרשאות"
+ "הרשאות אחרות של האפליקציה"
+ "בקשת הרשאה"
+ "זוהתה שכבת על במסך"
+ "כדי לשנות את הגדרת ההרשאה הזו, ראשית עליך לכבות את שכבת העל במסך ב\'הגדרות\' > \'אפליקציות\'"
+ "פתח הגדרות"
+ "Android Wear"
+ "פעולות התקנה/הסרת התקנה אינן נתמכות ב-Wear."
+ "בחר אילו הרשאות גישה ברצונך לתת ל <b>%1$s</b>"
+ "<b>%1$s</b> עודכנה. בחר אילו הרשאות גישה יהיו לאפליקציה זו."
+ "ביטול"
+ "המשך"
+ "הרשאות חדשות"
+ "הרשאות קיימות"
+ "מכין אפליקציה להתקנה…"
+ "לא ידוע"
+ "לצורכי אבטחה, הטאבלט שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."
+ "לצורכי אבטחה, מכשיר הטלוויזיה שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."
+ "לצורכי אבטחה, הטלפון שלך חסום להתקנת אפליקציות בלתי מוכרות המגיעות ממקור זה."
+ "נתוני הטלפון והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטלפון שלך בעקבות השימוש באפליקציה."
+ "נתוני הטאבלט והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטאבלט שלך בעקבות השימוש באפליקציה."
+ "נתוני הטלוויזיה והנתונים האישיים שלך חשופים יותר בפני התקפות על ידי אפליקציות ממקורות לא ידועים. אם תתקין אפליקציה זו, אתה מסכים לכך שאתה האחראי הבלעדי במקרה של אובדן נתונים או אם ייגרם נזק לטלוויזיה שלך בעקבות השימוש באפליקציה."
+ "המשך"
+ "הגדרות"
+ "מתקין/מסיר התקנה של אפליקציות Wear"
+
diff --git a/packages/PackageInstaller/res/values-ja-television/strings.xml b/packages/PackageInstaller/res/values-ja-television/strings.xml
new file mode 100644
index 0000000000000..9f31829615d3d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "許可しない(次回から表示しない)"
+ "これは後から[設定]>[アプリ]で変更できます"
+ "%1$s/%2$s"
+ "システムアプリの表示"
+ "アプリの権限"
+ "アプリの権限"
+ "%1$sの権限"
+ "その他の権限"
+ "%1$sの権限"
+
diff --git a/packages/PackageInstaller/res/values-ja-watch/strings.xml b/packages/PackageInstaller/res/values-ja-watch/strings.xml
new file mode 100644
index 0000000000000..735b908c2293b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "許可しない(今後表示しない)"
+ "%1$s/%2$s"
+ "システムアプリの表示"
+ "変更不可"
+ "はい"
+ "キャンセル"
+
diff --git a/packages/PackageInstaller/res/values-ja/strings.xml b/packages/PackageInstaller/res/values-ja/strings.xml
new file mode 100644
index 0000000000000..5a7dc7dffff67
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ja/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "パッケージインストーラ"
+ "次へ"
+ "インストール"
+ "完了"
+ "キャンセル"
+ "インストール中..."
+ "「%1$s」をインストールしています…"
+ "アプリをインストールしました。"
+ "このアプリケーションをインストールしてもよろしいですか?このアプリケーションは下記にアクセスする場合があります:"
+ "このアプリケーションをインストールしてもよろしいですか?このアプリケーションは特別なアクセス許可を必要としません。"
+ "この既存のアプリケーションへのアップデートをインストールしてもよろしいですか?既存のデータは失われません。アップデート後のアプリケーションは下記にアクセスする場合があります:"
+ "この内蔵アプリケーションへのアップデートをインストールしてもよろしいですか?既存のデータは失われません。アップデート後のアプリケーションは下記にアクセスする場合があります:"
+ "この既存のアプリにアップデートをインストールしますか?既存のデータが失われることはありません。特別なアクセス権も必要ありません。"
+ "この内蔵アプリにアップデートをインストールしますか?既存のデータが失われることはありません。特別なアクセス権も必要ありません。"
+ "アプリはインストールされていません。"
+ "パッケージのインストールはブロックされています。"
+ "パッケージが既存のパッケージと競合するため、アプリをインストールできませんでした。"
+ "お使いのタブレットに対応していないため、アプリをインストールできませんでした。"
+ "このアプリはお使いのテレビに対応していません。"
+ "お使いのスマートフォンに対応していないため、アプリをインストールできませんでした。"
+ "パッケージが無効の可能性があるため、アプリをインストールできませんでした。"
+ "%1$sをタブレットにインストールできませんでした。"
+ "%1$sをテレビにインストールできませんでした。"
+ "%1$sを端末にインストールできませんでした。"
+ "開く"
+ "提供元不明のアプリをインストールすることは、管理者により禁止されています"
+ "このユーザーは不明なアプリをインストールできません"
+ "このユーザーはアプリをインストールできません"
+ "OK"
+ "アプリを管理"
+ "容量不足です"
+ "%1$sをインストールできませんでした。空き容量を増やしてもう一度お試しください。"
+ "アプリが見つかりません"
+ "インストール済みアプリのリストに、このアプリはありません。"
+ "許可されていません"
+ "このアンインストール操作は現在のユーザーには許可されていません。"
+ "エラー"
+ "アプリをアンインストールできませんでした。"
+ "アプリをアンインストール"
+ "アップデートをアンインストール"
+ "%1$sは次のアプリの一部です:"
+ "このアプリをアンインストールしますか?"
+ "このアプリを""すべての""ユーザーからアンインストールしますか?このアプリとそのデータは端末の""すべての""ユーザーから削除されます。"
+ "%1$sさんのアプリをアンインストールしますか?"
+ "このアプリを出荷時の状態に戻しますか?データがすべて削除されます。"
+ "このアプリを出荷時の状態に戻しますか?データがすべて削除されます。これは、仕事用プロファイルを設定しているユーザーも含めて、この端末を使用するすべてのユーザーが対象となります。"
+ "実行中のアンインストール"
+ "エラーになったアンインストール"
+ "アンインストール中..."
+ "「%1$s」をアンインストールしています…"
+ "アンインストールが完了しました。"
+ "「%1$s」をアンインストールしました"
+ "アンインストールできませんでした。"
+ "「%1$s」をアンインストールできませんでした。"
+ "有効な端末管理アプリをアンインストールできません"
+ "%1$s さんの有効な端末管理アプリをアンインストールできません"
+ "このアプリは一部のユーザーやプロファイルに必要なため、アンインストールできませんでした"
+ "このアプリはプロファイルに必要なため、アンインストールできません。"
+ "このアプリは端末管理者が必要としているため、アンインストールできません。"
+ "端末管理アプリを管理"
+ "ユーザーを管理"
+ "%1$sをアンインストールできませんでした。"
+ "パッケージの解析中に問題が発生しました。"
+ "New"
+ "すべて"
+ "プライバシー"
+ "端末アクセス"
+ "このアップデートでは新たな許可は必要ありません。"
+ "許可しない"
+ "詳細"
+ "許可しない"
+ "%1$s/%2$s"
+ "<b>%1$s</b> に%2$sを許可しますか?"
+ "「%2$s」を <b>%1$s</b> に常に許可しますか?"
+ "アプリの使用中のみ"
+ "常時"
+ "許可しない(次回から表示しない)"
+ "%1$d 件無効"
+ "すべて無効"
+ "無効な権限なし"
+ "許可"
+ "アプリ"
+ "アプリの権限"
+ "今後表示しない"
+ "権限がありません"
+ "その他の権限"
+ "アプリ情報を開く"
+
+ 他%1$d件
+ 他%1$d件
+
+ "このアプリはAndroidの以前のバージョンを対象としています。権限を許可しないと、意図されたとおりに動作しなくなる可能性があります。"
+ "不明な操作の実行"
+ "%1$d/%2$d個のアプリを許可"
+ "システムを表示"
+ "システムを表示しない"
+ "アプリなし"
+ "位置情報の設定"
+ "%1$sはこの端末の位置情報サービスのプロバイダです。位置情報アクセスは位置情報の設定から変更できます。"
+ "この権限を許可しないと、お使いの端末の基本的な機能が意図されたとおりに動作しなくなる可能性があります。"
+ "ポリシーにより適用"
+ "バックグラウンドでのアクセスはポリシーによって無効です"
+ "バックグラウンドでのアクセスはポリシーによって有効です"
+ "フォアグラウンドでのアクセスはポリシーによって有効です"
+ "管理者により管理されています"
+
+
+
+ "常時"
+ "アプリの使用中のみ"
+ "なし"
+ "読み込んでいます…"
+ "すべての権限"
+ "その他のアプリ機能"
+ "権限のリクエスト"
+ "画面オーバーレイを検出"
+ "この権限設定を変更するには、まず[設定]>[アプリ]から画面オーバーレイをOFFにします"
+ "設定を開く"
+ "Android Wear"
+ "Wear ではインストールやアンインストールができません"
+ "「<b>%1$s</b>」に許可する権限の選択"
+ "「<b>%1$s</b>」が更新されました。このアプリに許可する権限を選択してください。"
+ "キャンセル"
+ "続行"
+ "新しい権限"
+ "現在の権限"
+ "アプリを準備しています…"
+ "不明"
+ "セキュリティ上の理由から、お使いのタブレットではこの提供元からの不明なアプリをインストールすることはできません。"
+ "セキュリティ上の理由から、お使いのテレビではこの提供元からの不明なアプリをインストールすることはできません。"
+ "セキュリティ上の理由から、お使いのスマートフォンではこの提供元からの不明なアプリをインストールすることはできません。"
+ "不明なアプリをインストールするとスマートフォンや個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性があるスマートフォンへの損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"
+ "不明なアプリをインストールするとタブレットや個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性があるタブレットへの損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"
+ "不明なアプリをインストールすると TV や個人データが攻撃を受ける可能性が高くなります。このアプリをインストールすることにより、アプリの使用により生じる可能性がある TV への損害やデータの損失について、ユーザーご自身が単独で責任を負うことに同意するものとします。"
+ "続行"
+ "設定"
+ "Wear アプリのインストールとアンインストール"
+
diff --git a/packages/PackageInstaller/res/values-ka-television/strings.xml b/packages/PackageInstaller/res/values-ka-television/strings.xml
new file mode 100644
index 0000000000000..b7d03b05f7a88
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "უარყავი და აღარ მკითხო"
+ "ამის შეცვლა მოგვიანებით შეგიძლიათ სექციაში პარამეტრები > აპები"
+ "%1$s / %2$s"
+ "სისტემის აპების ჩვენება"
+ "აპის ნებართვები"
+ "აპის ნებართვები"
+ "ნებართვები (%1$s)"
+ "დამატებითი ნებართვები"
+ "ნებართვები (%1$s)"
+
diff --git a/packages/PackageInstaller/res/values-ka-watch/strings.xml b/packages/PackageInstaller/res/values-ka-watch/strings.xml
new file mode 100644
index 0000000000000..15749127ca790
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "უარყავი და აღარ მკითხო"
+ "%1$s / %2$s"
+ "სისტემის აპების ჩვენება"
+ "ვერ შეიცვლება"
+ "დიახ"
+ "გაუქმება"
+
diff --git a/packages/PackageInstaller/res/values-ka/strings.xml b/packages/PackageInstaller/res/values-ka/strings.xml
new file mode 100644
index 0000000000000..997f95ce2208a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ka/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "პაკეტის ინსტალერი"
+ "შემდეგი"
+ "დაყენება"
+ "დასრულდა"
+ "გაუქმება"
+ "მიმდინარეობს ინსტალაცია…"
+ "მიმდინარეობს %1$s-ის ინსტალაცია…"
+ "აპი დაყენებულია."
+ "გსურთ, ამ აპლიკაციის დაყენება? მას ექნება წვდომა:"
+ "გსურთ ამ აპლიკაციის დაყენება? ის არ მოითხოვს რაიმე განსაკუთრებულ ნებართვას."
+ "გსურთ განახლების დაყენება ამ არსებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. განახლებულ აპლიკაციას წვდომა ექნება:"
+ "გსურთ განახლების დაყენება ამ ჩაშენებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. განახლენულ აპლიკაციას წვდომა ექნება:"
+ "გსურთ განახლების დაყენება ამ არსებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. ის არ საჭიროებს რაიმე განსაკუთრებულ წვდომას:"
+ "გსურთ განახლების დაყენება ამ ჩაშენებული აპლიკაციისთვის? არსებული მონაცემები არ დაიკარგება. ის არ საჭიროებს რაიმე განსაკუთრებულ წვდომას:"
+ "აპი არ არის დაყენებული."
+ "ამ პაკეტის ინსტალაცია დაბლოკილია."
+ "აპი ვერ დაინსტალირდა, რადგან პაკეტი კონფლიქტშია არსებულ პაკეტთან."
+ "აპი ვერ დაინსტალირდა, რადგან ის არ არის თავსებადი თქვენს ტაბლეტთან."
+ "ეს აპი არ არის თავსებადი თქვენს ტელევიზორთან."
+ "აპი ვერ დაინსტალირდა, რადგან ის არ არის თავსებადი თქვენს ტელეფონთან."
+ "აპი ვერ დაინსტალირდა, რადგან პაკეტი, სავარაუდოდ, არასწორია."
+ "%1$s-ის დაყენება თქვენს ტაბლეტზე ვერ მოხერხდა."
+ "%1$s ვერ დაინსტალირდება თქვენს ტელევიზორში."
+ "%1$s-ის დაყენება თქვენს ტელეფონზე ვერ მოხერხდა."
+ "გახსნა"
+ "უცნობი წყაროებიდან ჩამოტვირთული აპების ინსტალაცია თქვენი ადმინისტრატორის მიერ ნებადართული არ არის"
+ "ამ მომხმარებელს უცნობი აპების ინსტალაცია არ შეუძლია"
+ "ამ მომხმარებელს აპების დაინსტალირების უფლება არ აქვს"
+ "კარგი"
+ "აპების მართვა"
+ "სივრცე შეივსო"
+ "%1$s-ის დაყენება შეუძლებელია. გაათავისუფლეთ მეხსიერება და სცადეთ ხელახლა."
+ "აპი ვერ მოიძებნა."
+ "დაყენებული აპების სიაში ეს აპი ვერ მოიძებნა."
+ "დაუშვებელია"
+ "მიმდინარე მომხმარებელს არ აქვს დეინსტალაციის განხორციელების უფლება."
+ "შეცდომა"
+ "აპის დეინსტალაცია ვერ მოხერხდა."
+ "აპის დეინსტალაცია"
+ "განახლების დეინსტალაცია"
+ "%1$s არის შემდეგი აპის ნაწილი:"
+ "გსურთ, ამ აპის დეინსტალაცია?"
+
+ "გსურთ %1$s მომხმარებლისათვის ამ აპის დეინსტალაცია?"
+ "გსურთ ამ აპის ქარხნული ვერსიით ჩანაცვლება? მონაცემები მთლიანად ამოიშლება."
+ "გსურთ ამ აპის ქარხნული ვერსიით ჩანაცვლება? მონაცემები მთლიანად ამოიშლება. ეს მოქმედება გავლენას იქონიებს ამ მოწყობილობის ყველა მომხმარებელზე, მათ შორის, სამსახურის პროფილებით მოსარგებლეებზეც."
+ "გაშვებული დეინსტალაციები"
+ "შეუსრულებელი დეინსტალაციები"
+ "დეინსტალაცია…"
+ "მიმდინარეობს %1$s-ის დეინსტალაცია…"
+ "დეინსტალაცია დასრულდა."
+ "%1$s დეინსტალირებულია"
+ "დეინსტალაცია წარუმატებელია."
+ "%1$s-ის დეინსტალაცია ვერ მოხერხდა."
+ "მოწყობილობის ადმინისტრატორის აქტიური აპის დეინსტალაცია ვერ მოხერხდება"
+ "%1$s-სთვის მოწყობილობის ადმინისტრატორის აქტიური აპის დეინსტალაცია ვერ მოხერხდება"
+ "ამ აპს მომხმარებლების/პროფილების ნაწილი იყენებს. სხვებისთვის ის დეინსტალირებულია."
+ "ეს აპი საჭიროა თქვენი პროფილისთვის. მისი დეინსტალაცია ვერ მოხერხდება."
+ "ეს აპი საჭიროა თქვენი მოწყ. ადმინისტრატორისათვის და დეინსტალაცია ვერ გამოვა."
+ "მოწყობილობის ადმინისტრატორების აპების მართვა"
+ "მომხმარებლების მართვა"
+ "%1$s -ის დეინსტალაცია ვერ მოხერხდა."
+ "პაკეტის ანალიზისას წარმოიშვა პრობლემა."
+ "ახალი"
+ "ყველა"
+ "კონფიდენციალურობა"
+ "მოწყობილობის წვდომა"
+ "ეს განახლება არ საჭიროებს ახალ ნებართვებს."
+ "უარყოფა"
+ "დამატებითი ინფორმაცია"
+ "მაინც უარყოფა"
+ "%1$s / %2$s-დან"
+ "გსურთ, დაუშვათ, რომ <b>%1$s</b>-მ შეასრულოს %2$s?"
+ "გსურთ, ყოველთვის შესრულდეს %2$s <b>%1$s-ის</b> მიერ?"
+ "მხოლოდ აპის გამოყენებისას"
+ "ყოველთვის"
+ "უარყავი და აღარ მკითხო"
+ "გათიშულია %1$d"
+ "გათიშულია ყველა"
+ "არაფერია გათიშული"
+ "დაშვება"
+ "აპები"
+ "აპის უფლებები"
+ "აღარ მკითხოთ"
+ "ნებართვები არ არის"
+ "დამატებითი ნებართვები"
+ "აპის ინფორმაციის გახსნა"
+
+ %1$d კიდევ
+ %1$d კიდევ
+
+ "ეს აპი Android-ის ძველი ვერსიისთვის შეიქმნა. ნებართვის უარყოფამ შესაძლოა მისი არასათანადო ფუნქციონირება გამოიწვიოს."
+ "უცნობი ქმედების შესრულება"
+ "დაშვებულია %1$d/%2$d აპიდან"
+ "სისტემის ჩვენება"
+ "სისტემური პროცესების დამალვა"
+ "აპები არ არის"
+ "მდებარეობის პარამეტრები"
+ "%1$s არის მდებარეობის სერვისების მომწოდებელი ამ მოწყობილობისთვის. მდებარეობაზე წვდომის შეცვლა შესაძლებელია მდებარეობის პარამეტრებიდან."
+ "ამ ნებართვის უარყოფის შემთხვევაში, თქვენი მოწყობილობის ძირითადმა ფუნქციებმა შესაძლოა სათანადოდ აღარ იმუშაოს."
+ "შეესაბამება წესს"
+ "ფონზე წვდომა დებულებით გათიშულია"
+ "ფონზე წვდომა დებულებით დაშვებულია"
+ "წინა პლანზე წვდომა დებულებით დაშვებულია"
+ "იმართება ადმინისტრატორის მიერ"
+
+
+
+ "ყოველთვის"
+ "მხოლოდ აპის გამოყენებისას"
+ "არასოდეს"
+ "იტვირთება..."
+ "ყველა ნებართვა"
+ "აპის სხვა შესაძლებლობები"
+ "ნებართვის მოთხოვნა"
+ "ეკრანის გადაფარვა გამოვლინდა"
+ "ამ ნებართვის პარამეტრის შესაცვლელად, ჯერ უნდა გამორთოთ ეკრანის გადაფარვა პარამეტრებიდან > აპებიდან"
+ "პარამეტრების გახსნა"
+ "Android Wear"
+ "ინსტალაციის/დეინსტალაციის მოქმედებები არ არის მხარდაჭერილი Wear-ზე."
+ "აირჩიეთ, რაზე ჰქონდეს წვდომა <b>%1$s</b>-ს"
+ "<b>%1$s</b> განახლდა. აირჩიეთ, რაზე ჰქონდეს წვდომა ამ აპს."
+ "გაუქმება"
+ "გაგრძელება"
+ "ახალი ნებართვები"
+ "ამჟამინდელი ნებართვები"
+ "მიმდინარეობს აპის შუალედური შენახვა…"
+ "უცნობი"
+ "თქვენივე უსაფრთხოებისთვის, ტაბლეტს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."
+ "თქვენივე უსაფრთხოებისთვის, ტელევიზორს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."
+ "თქვენივე უსაფრთხოებისთვის, ტელეფონს ამ წყაროდან უცნობი აპების ინსტალაციის უფლება არ აქვს."
+ "თქვენი ტელეფონი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტელეფონისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."
+ "თქვენი ტელეფონი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტაბლეტისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."
+ "თქვენი ტელევიზორი და პირადი მონაცემები უცნობი აპების შემოტევების წინაშე მეტად დაუცველია. ამ აპის ინსტალაციის შემთხვევაში, თქვენ თანახმა ხართ, პასუხისმგებელი იყოთ მისი გამოყენების შედეგად ტელევიზორისთვის მიყენებულ ზიანსა და მონაცემების დაკარგვაზე."
+ "გაგრძელება"
+ "პარამეტრები"
+ "Wear აპების ინსტალაცია/დეინსტალაცია"
+
diff --git a/packages/PackageInstaller/res/values-kk-television/strings.xml b/packages/PackageInstaller/res/values-kk-television/strings.xml
new file mode 100644
index 0000000000000..2d0ff437eb11d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Тыйым салу және қайтадан сұрамау"
+ "Мұны кейінірек \"Параметрлер\" және \"Қолданбалар\" ішінен өзгертуге болады"
+ "%1$s / %2$s"
+ "Жүйелік қолданбаларды көрсету"
+ "Қолданба рұқсаттары"
+ "Қолданба рұқсаттары"
+ "%1$s рұқсаттары"
+ "Қосымша рұқсаттар"
+ "%1$s рұқсаттары"
+
diff --git a/packages/PackageInstaller/res/values-kk-watch/strings.xml b/packages/PackageInstaller/res/values-kk-watch/strings.xml
new file mode 100644
index 0000000000000..8996bcbe1e52b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Тыйым салу, қайтадан сұрамау"
+ "%1$s / %2$s"
+ "Жүйелік қолданбаларды көрсету"
+ "Өзгерту мүмкін емес"
+ "Иә"
+ "Бас тарту"
+
diff --git a/packages/PackageInstaller/res/values-kk/strings.xml b/packages/PackageInstaller/res/values-kk/strings.xml
new file mode 100644
index 0000000000000..26892e1ede7df
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kk/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Бума орнатқыш"
+ "Келесі"
+ "Орнату"
+ "Дайын"
+ "Бас тарту"
+ "Орнатуда…"
+ "%1$s орнатылуда…"
+ "Қолданба орнатылды."
+ "Бұл қолданбаны орнатуды қалайсыз ба? Оның келесі нәрселерге қол жетімділігі болады:"
+ "Бұл қолданбаны орнатуды қалайсыз ба? Ол ерекше қол жетімділікті қажет етпейді."
+ "Бұл қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Жаңартылған қолданбаның келесі нәрселерге қол жетімділігі болады:"
+ "Бұл орнатылған қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Жаңартылған қолданбаның келесі нәрселерге қол жетімділігі болады:"
+ "Бұл қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Ол ерекше қол жетімділікті қажет етпейді."
+ "Бұл орнатылған қолданбаның жаңартылған нұсқасын орнатуды қалайсыз ба? Деректеріңіз жоғалмайды. Ол ерекше қол жетімділікті қажет етпейді."
+ "Қолданба орнатылмады."
+ "Буманы орнатуға тыйым салынды."
+ "Пакет түрінде орнатылмаған қолданба мен бұрыннан бар пакеттің арасында қайшылық туындайды."
+ "Қолданба түрінде орнатылмаған қолданба, планшетіңізбен үйлесімді емес."
+ "Бұл қолданба теледидарыңызбен үйлесімді емес."
+ "Қолданба түрінде орнатылмаған қолданба, телефоныңызбен үйлесімді емес."
+ "Пакет түрінде орнатылмаған қолданба жарамсыз болып табылады."
+ "%1$s қолданбасын планшетіңізге орнату мүмкін емес"
+ "%1$s теледидарыңызда орнату мүмкін емес."
+ "%1$s қолданбасын телефоныңызға орнату мүмкін емес."
+ "Ашу"
+ "Әкімші белгісіз көздерден алынған қолданбаларды орнатуға рұқсат етпейді"
+ "Бұл пайдаланушы белгісіз қолданбаларды орната алмайды"
+ "Бұл пайдаланушының қолданбаларды орнату рұқсаты жоқ"
+ "Жарайды"
+ "Қолданбаларды басқару"
+ "Орнында емес"
+ "%1$s қолданбасын орнату мүмкін болмады. Орын босатып, қайта әрекеттеніп көріңіз."
+ "Қолданба табылмады"
+ "Қолданба орнатылған қолданбалар тізімінен табылмады."
+ "Рұқсат етілмеген"
+ "Ағымдағы пайдаланушыға бұл жою әрекетіне рұқсат берілмеген."
+ "Қате"
+ "Қолданба жойылмады."
+ "Қолданбаны алып тастау"
+ "Жаңартуды алып тастау"
+ "%1$s келесі қолданбаның бөлігі:"
+ "Бұл қолданбаны алып тастауды қалайсыз ба?"
+ "Бұл қолданбаны ""барлық"" пайдаланушылар үшін алып тастауды қалайсыз ба? Қолданба және оның деректері құрылғыдағы ""барлық"" пайдаланушылардан алынады."
+ "Пайдаланушы %1$s үшін осы қолданбаны жою керек пе?"
+ "Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Бүкіл деректер жойылады."
+ "Осы қолданбаны зауыттық нұсқамен ауыстыру керек пе? Бүкіл деректер жойылады. Бұл осы құрылғының барлық пайдаланушыларына, соның ішінде жұмыс профильдері бар пайдаланушыларға әсер етеді."
+ "Жұмыс істеп тұрған жою әрекеттері"
+ "Сәтсіз жою әрекеттері"
+ "Алып тастау орындалуда..."
+ "%1$s жойылуда…"
+ "Алып тастау аяқталды."
+ "%1$s жойылды"
+ "Алып тастау сәтсіздікке ұшырады."
+ "%1$s жою сәтсіз аяқталды."
+ "Белсенді құрылғының әкімші қолданбасын жою мүмкін емес"
+ "%1$s үшін белсенді құрылғының әкімші қолданбасын жою мүмкін емес"
+ "Бұл қолданба кейбір пайдаланушылар немесе профильдер үшін қажет және басқалар үшін жойылды"
+ "Бұл қолданба профиліңіз үшін қажет және оны жою мүмкін емес."
+ "Әкімші осы қолданбаны талап етеді және оны жою мүмкін емес."
+ "Құрылғы әкімшісі қолданбаларын басқару"
+ "Пайдаланушыларды басқару"
+ "%1$s қолданбасын алып тастау мүмкін емес."
+ "Жинақты сараптау кезінде мәселе орын алды."
+ "Жаңа"
+ "Барлық"
+ "Құпиялылық"
+ "Құралға кіру"
+ "Бұл қолданба жаңа рұқсаттарды қажет етпейді."
+ "Тыйым салу"
+ "Қосымша ақпарат"
+ "Бәрібір рұқсат бермеу"
+ "%2$s/%1$s"
+ "<b>%1$s</b> қолданбасына %2$s рұқсатын беру керек пе?"
+ "<b>%1$s</b> қолданбасы үшін %2$s әрекетіне әрқашан рұқсат етілсін бе?"
+ "Қолданба пайдаланылғанда ғана"
+ "Әрқашан"
+ "Тыйым салынсын және қайта сұралмасын"
+ "%1$d рұқсат өшірілді"
+ "барлық рұқсаттар өшірілді"
+ "рұқсаттардың ешқайсысы өшірілмеді"
+ "Рұқсат беру"
+ "Қолданбалар"
+ "Қолданба рұқсаттары"
+ "Қайта сұралмасын"
+ "Рұқсат жоқ"
+ "Қосымша рұқсаттар"
+ "Қолданба ақпаратын ашу"
+
+ Тағы %1$d
+ Тағы %1$d
+
+ "Бұл қолданба Android жүйесінің ескі нұсқасына арналған. Рұқсаттан бас тартсаңыз, бұдан былай тиісінше жұмыс істемеуі мүмкін."
+ "белгісіз әрекетті орындау"
+ "%1$d/%2$d қолданбаға рұқсат етілген"
+ "Жүйені көрсету"
+ "Жүйені жасыру"
+ "Қолданбалар жоқ"
+ "Орынды анықтау параметрлері"
+ "%1$s — осы құрылғыға орынды анықтау қызметтерін көрсететін қолданба. Орынды пайдалану мүмкіндігін орынды анықтау параметрлерінде өзгертуге болады."
+ "Бұл рұқсатты бермесеңіз, құрылғының негізгі функциялары енді көзделгендей жұмыс істемеуі мүмкін."
+ "Саясат арқылы қолданылған"
+ "Саясат бойынша фондық режимде кіруге рұқсат етілмеген"
+ "Саясат бойынша фондық режимде кіруге рұқсат етілген"
+ "Саясат бойынша экрандық режимде кіруге рұқсат етілген"
+ "Әкімші басқарады"
+
+
+
+ "Әрқашан"
+ "Қолданба пайдаланылғанда ғана"
+ "Ешқашан"
+ "Жүктелуде…"
+ "Барлық рұқсаттар"
+ "Басқа қолданба мүмкіндіктері"
+ "Рұқсат сұрау"
+ "Экранды қабаттастыру анықталды"
+ "Бұл рұқсат параметрін өзгерту үшін алдымен «Параметрлер» > «Қолданбалар» тармағында экранды қабаттастыруды өшіру керек"
+ "Параметрлерді ашу"
+ "Android Wear"
+ "Wear құрылғысында \"Орнату\"/\"Жою\" әрекеттері қолданылмайды."
+ "%1$s қолданбасы қайда кіре алатынын таңдаңыз"
+ "%1$s қолданбасы жаңартылды. Бұл қолданбаның қайда кіре алатынын таңдаңыз."
+ "Бас тарту"
+ "Жалғастыру"
+ "Жаңа рұқсаттар"
+ "Ағымдағы рұқсаттар"
+ "Қолданба реттелуде…"
+ "Белгісіз"
+ "Қауіпсіздікті сақтау үшін планшетке бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."
+ "Қауіпсіздікті сақтау үшін теледидарға бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."
+ "Қауіпсіздікті сақтау үшін телефонға бұл дереккөзден белгісіз қолданбаларды орнатуға рұқсат берілмейді."
+ "Телефон және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі телефонға келетін залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."
+ "Планшет және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі планшетке келетін залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."
+ "Теледидар және жеке деректер белгісіз қолданбалардың шабуылына ұшырауы мүмкін. Бұл қолданбаны орнату арқылы оны пайдалану нәтижесіндегі теледидарға келетін қандай да бір залалға немесе деректердің жоғалуына өзіңіз ғана жауапты болатыныңызға келісесіз."
+ "Жалғастыру"
+ "Параметрлер"
+ "Wear қолданбасын орнату/жою"
+
diff --git a/packages/PackageInstaller/res/values-km-television/strings.xml b/packages/PackageInstaller/res/values-km-television/strings.xml
new file mode 100644
index 0000000000000..f02744dbb4940
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "បដិសេធ ហើយកុំសួរម្តងទៀត"
+ "អ្នកអាចប្តូរវាពេលក្រោយនៅក្នុងការកំណត់ > កម្មវិធី"
+ "%1$s / %2$s"
+ "បង្ហាញកម្មវិធីប្រព័ន្ធ"
+ "សិទ្ធិអនុញ្ញាតកម្មវិធី"
+ "សិទ្ធិអនុញ្ញាតកម្មវិធី"
+ "សិទ្ធិអនុញ្ញាត %1$s"
+ "ការអនុញ្ញាតបន្ថែម"
+ "សិទ្ធិអនុញ្ញាត %1$s"
+
diff --git a/packages/PackageInstaller/res/values-km-watch/strings.xml b/packages/PackageInstaller/res/values-km-watch/strings.xml
new file mode 100644
index 0000000000000..c5f49c54630fd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "បដិសេធ សូមកុំសួរម្តងទៀត"
+ "%1$s / %2$s"
+ "បង្ហាញកម្មវិធីប្រព័ន្ធ"
+ "មិនអាចប្តូរបានទេ"
+ "បាទ/ចាស"
+ "បោះបង់"
+
diff --git a/packages/PackageInstaller/res/values-km/strings.xml b/packages/PackageInstaller/res/values-km/strings.xml
new file mode 100644
index 0000000000000..693ea32792e21
--- /dev/null
+++ b/packages/PackageInstaller/res/values-km/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "កម្មវិធីដំឡើងកញ្ចប់"
+ "បន្ទាប់"
+ "ដំឡើង"
+ "រួចរាល់"
+ "បោះបង់"
+ "កំពុងដំឡើង..."
+ "កំពុងដំឡើង %1$s…"
+ "បានដំឡើងកម្មវិធី។"
+ "តើអ្នកចង់ដំឡើងកម្មវិធីនេះ? វានឹងមានសិទ្ធិចូល៖"
+ "តើអ្នកចង់ដំឡើងកម្មវិធីនេះ? វាមិនទាមទារសិទ្ធិចូលពិសេសទេ។"
+ "តើអ្នកចង់ដំឡើងបច្ចុប្បន្នភាពកម្មវិធីដែលមានស្រាប់នេះ? ទិន្នន័យដែលមានស្រាប់របស់អ្នកនឹងមិនបាត់បង់ទេ។ កម្មវិធីបានធ្វើបច្ចុប្បន្នភាពនឹងចូលដំណើរការទៅ៖"
+ "តើអ្នកចង់ដំឡើងបច្ចុប្បន្នភាពកម្មវិធីដែលជាប់ជាមួយនេះ? ទិន្នន័យដែលមានស្រាប់របស់អ្នកនឹងមិនបាត់បង់ទេ។ កម្មវិធីបានធ្វើបច្ចុប្បន្នភាពហើយនឹងមានសិទ្ធិចូល៖"
+ "តើអ្នកចង់ដំឡើងបច្ចុប្បន្នភាពកម្មវិធីដែលមានស្រាប់នេះ? ទិន្នន័យដែលមានស្រាប់របស់អ្នកនឹងមិនបាត់បង់ទេ។ វាមិនទាមទារការចូលដំណើរការពិសេសណាមួយទេ។"
+ "តើអ្នកចង់ដំឡើងបច្ចុប្បន្នភាពកម្មវិធីដែលជាប់ជាមួយ? ទិន្នន័យដែលមានស្រាប់របស់អ្នកនឹងមិនបាត់បង់ទេ។ វាមិនទាមទារការចូលដំណើរការពិសេសណាមួយទេ។"
+ "មិនបានដំឡើងកម្មវិធី។"
+ "កញ្ចប់នេះត្រូវបានរារាំងមិនឲ្យដំឡើង"
+ "កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់កម្មវិធីមិនត្រូវគ្នាជាមួយកញ្ចប់ដែលមានស្រាប់។"
+ "កម្មវិធីមិនបានដំឡើងទេ ដោយសារកម្មវិធីមិនត្រូវគ្នាជាមួយថេប្លេតរបស់អ្នក។"
+ "កម្មវិធីនេះមិនត្រូវគ្នាជាមួយទូរទស្សន៍របស់អ្នកទេ"
+ "កម្មវិធីមិនបានដំឡើងទេ ដោយសារកម្មវិធីមិនត្រូវគ្នាជាមួយទូរសព្ទរបស់អ្នក។"
+ "កម្មវិធីមិនបានដំឡើងទេ ដោយសារកញ្ចប់គ្មានសុពលភាព។"
+ "មិនអាចដំឡើង %1$s ក្នុងកុំព្យូទ័របន្ទះរបស់អ្នក។"
+ "%1$s មិនអាចដំឡើងនៅលើទូរទស្សន៍របស់អ្នកទេ។"
+ "មិនអាចដំឡើង %1$s ក្នុងទូរស័ព្ទរបស់អ្នក។"
+ "បើក"
+ "អ្នកគ្រប់គ្រងរបស់អ្នកមិនអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលបានមកពីប្រភពដែលមិនស្គាល់ទេ"
+ "កម្មវិធីដែលមិនស្គាល់មិនអាចដំឡើងដោយអ្នកប្រើប្រាស់នេះបានទេ"
+ "អ្នកប្រើប្រាស់នេះមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីទេ"
+ "យល់ព្រម"
+ "គ្រប់គ្រងកម្មវិធី"
+ "អស់ទំហំ"
+ "មិនអាចដំឡើង %1$s ។ លុបឯកសារខ្លះ ហើយព្យាយាមម្ដងទៀត។"
+ "រកមិនឃើញកម្មវិធី"
+ "រកមិនឃើញកម្មវិធីក្នុងបញ្ជីកម្មវិធីបានដំឡើង។"
+ "មិនបានអនុញ្ញាត"
+ "អ្នកប្រើបច្ចុប្បន្នមិនមានការអនុញ្ញាតឱ្យធ្វើការលុបនេះទេ។"
+ "បញ្ហា"
+ "មិនអាចលុបកម្មវិធីនេះបានទេ។"
+ "លុបកម្មវិធី"
+ "លុបបច្ចុប្បន្នភាព"
+ "%1$s ជាផ្នែកមួយនៃកម្មវិធីដូចខាងក្រោមនេះ៖"
+ "តើអ្នកចង់លុបកម្មវិធីនេះឬ?"
+ "តើអ្នកចង់លុបកម្មវិធីនេះសម្រាប់អ្នកប្រើ ""ទាំងអស់""? កម្មវិធី និងទិន្នន័យរបស់វានឹងត្រូវបានលុបចេញពីអ្នកប្រើ ""ទាំងអស់"" ក្នុងឧបករណ៍នេះ។"
+ "តើអ្នកចង់លុបកម្មវិធីនេះសម្រាប់អ្នកប្រើ %1$s?"
+ "ជំនួសកម្មវិធីនេះដោយកំណែរោងចក្រឬ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុបចេញ។"
+ "ជំនួយកម្មវិធីនេះដោយកំណែរោងចក្រឬ? ទិន្នន័យទាំងអស់នឹងត្រូវបានលុបចេញ។ វាប៉ះពាល់ដល់អ្នកប្រើឧបករណ៍នេះទាំងអស់ ដោយរាប់បញ្ចូលទាំងអ្នកប្រើដែលមានប្រវត្តិរូបការងារផងដែរ។"
+ "កំពុងដំណើរការការលុប"
+ "មិនអាចធ្វើការលុបបានទេ"
+ "កំពុងលុប..."
+ "កំពុងលុប %1$s…"
+ "បានបញ្ចប់ការលុប។"
+ "បានលុប %1$s"
+ "ការលុបបរាជ័យ។"
+ "មិនអាចលុប %1$s បាន។"
+ "មិនអាចលុបកម្មវិធីអ្នកគ្រប់គ្រងឧបករណ៍ដែលបានដំណើរការបានទេ"
+ "មិនអាចលុបកម្មវិធីអ្នកគ្រប់គ្រងឧបករណ៍សម្រាប់ %1$s ដែលបានដំណើរការបានទេ"
+ "កម្មវិធីនេះតម្រូវឲ្យមានសម្រាប់អ្នកប្រើ ឬប្រវត្តិរូបមួយចំនួន និងត្រូវបានលុបសម្រាប់អ្នកប្រើផ្សេងទៀត"
+ "កម្មវិធីនេះចាំបាច់សម្រាប់ប្រវតិ្តការងាររបស់អ្នក ហើយវាមិនអាចលុបបានទេ។"
+ "កម្មវិធីនេះត្រូវបានទាមទារដោយអ្នកគ្រប់គ្រងឧបករណ៍របស់អ្នក ហើយមិនអាចលុបចេញបានទេ។"
+ "គ្រប់គ្រងកម្មវិធីអ្នកគ្រប់គ្រងឧបករណ៍"
+ "គ្រប់គ្រងអ្នកប្រើ"
+ "មិនអាចលុប %1$s ។"
+ "មានបញ្ហាក្នុងការញែកកញ្ចប់។"
+ "ថ្មី"
+ "ទាំងអស់"
+ "ភាពឯកជន"
+ "ការចូលដំណើរការឧបករណ៍"
+ "បច្ចុប្បន្នភាពនេះមិនទាមទារសិទ្ធិថ្មីទេ។"
+ "បដិសេធ"
+ "ព័ត៌មានបន្ថែម"
+ "បដិសេធទោះយ៉ាងណាក៏ដោយ"
+ "%1$s នៃ %2$s"
+ "អនុញ្ញាតឲ្យ <b>%1$s</b> %2$s?"
+ "អនុញ្ញាតឱ្យ <b>%1$s</b> %2$s ជានិច្ចមែនទេ?"
+ "ខណៈពេលប្រើកម្មវិធីតែប៉ុណ្ណោះ"
+ "ជានិច្ច"
+ "បដិសេធ ហើយកុំសួរម្តងទៀត"
+ "បានបិទ %1$d"
+ "បានបិទទាំងអស់"
+ "មិនបានបិទអ្វីទាំងអស់"
+ "អនុញ្ញាត"
+ "កម្មវិធី"
+ "ការអនុញ្ញាតកម្មវិធី"
+ "កុំសួរទៀត"
+ "គ្មានសិទ្ធិអនុញ្ញាត"
+ "ការអនុញ្ញាតបន្ថែម"
+ "បើកព័ត៌មានកម្មវិធី"
+
+ %1$d ទៀត
+ %1$d ទៀត
+
+ "កម្មវិធីនេះត្រូវបានរចនាឡើងសម្រាប់កំណែចាស់របស់ Android។ ការបដិសេធសិទ្ធិអនុញ្ញាតអាចបណ្តាលឲ្យវាបំពេញមុខងារមិនដូចអ្វីដែលគេរំពឹងទុកតទៅទៀតទេ។"
+ "ប្រតិបត្តិការសកម្មភាពមិនស្គាល់"
+ "បានអនុញ្ញាតកម្មវិធី %1$d នៃ %2$d"
+ "បង្ហាញប្រព័ន្ធ"
+ "លាក់ប្រព័ន្ធ"
+ "គ្មានកម្មវិធី"
+ "ការកំណត់ទីតាំង"
+ "%1$s គឺជាអ្នកផ្តល់សេវាកម្មទីតាំងសម្រាប់ឧបករណ៍នេះ។ ការចូលដំណើរការទីតាំងអាចកែសម្រួលបានចេញពីការកំណត់ទីតាំង។"
+ "ប្រសិនបើអ្នកបដិសេធសិទ្ធិអនុញ្ញាតនេះ លក្ខណៈពិសេសគោលនៃឧបករណ៍របស់អ្នកអាចមិនដំណើរការដូចដែលអ្នកចង់បានតទៅទៀតទេ។"
+ "អនុវត្តតាមគោលការណ៍"
+ "ការចូលប្រើផ្ទៃខាងក្រោយត្រូវបានបិទដោយគោលការណ៍"
+ "ការចូលប្រើផ្ទៃខាងក្រោយត្រូវបានបើកដោយគោលការណ៍"
+ "ការចូលប្រើផ្ទៃខាងមុខត្រូវបានបើកដោយគោលការណ៍"
+ "គ្រប់គ្រងដោយអ្នកគ្រប់គ្រង"
+
+
+
+ "ជានិច្ច"
+ "ខណៈពេលប្រើកម្មវិធីតែប៉ុណ្ណោះ"
+ "កុំឱ្យសោះ"
+ "កំពុងដំណើរការ..."
+ "សិទ្ធិអនុញ្ញាតទាំងអស់"
+ "សមត្ថភាពកម្មវិធីផ្សេងទៀត"
+ "សំណើសុំសិទ្ធិអនុញ្ញាត"
+ "បានរកឃើញអេក្រង់ត្រួតគ្នា"
+ "ដើម្បីប្តូរការកំណត់សិទ្ធិអនុញ្ញាតនេះ ជាដំបូងអ្នកត្រូវបិទអេក្រង់ត្រួតគ្នានៅក្នុង ការកំណត់ > កម្មវិធី"
+ "បើកការកំណត់"
+ "Android Wear"
+ "សកម្មភាពដំឡើង/លុបការដំឡើងមិនគាំទ្រនៅលើ Wear ទេ"
+ "ជ្រើសរើសអ្វីដែលត្រូវអនុញ្ញាតឲ្យ <b>%1$s</b> ចូលដំណើរការ"
+ "<b>%1$s</b> ត្រូវបានអាប់ដេត។ ជ្រើសរើសអ្វីដែលត្រូវអនុញ្ញាតឲ្យកម្មវិធីនេះចូលដំណើរការ។"
+ "បោះបង់"
+ "បន្ត"
+ "សិទ្ធិអនុញ្ញាតថ្មី"
+ "សិទ្ធិអនុញ្ញាតបច្ចុប្បន្ន"
+ "កំពុងសាកល្បងកម្មវិធី…"
+ "មិនស្គាល់"
+ "ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ថេប្លេតរបស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"
+ "ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ទូរទស្សន៍របស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"
+ "ដើម្បីការពារសុវតិ្ថភាពរបស់អ្នក ទូរសព្ទរបស់អ្នកមិនត្រូវបានអនុញ្ញាតឲ្យដំឡើងកម្មវិធីដែលមិនស្គាល់ពីប្រភពនេះទេ។"
+ "ទូរសព្ទ និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះទូរសព្ទ ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"
+ "ថេប្លេត និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះថេប្លេត ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"
+ "ទូរទស្សន៍ និងទិន្នន័យផ្ទាល់ខ្លួនរបស់អ្នកងាយនឹងរងគ្រោះពីការវាយប្រហារពីកម្មវិធីដែលមិនស្គាល់។ ប្រសិនបើដំឡើងកម្មវិធីនេះ មានន័យថាអ្នកទទួលខុសត្រូវលើការខូចខាតណាមួយចំពោះទូរទស្សន៍ ឬការបាត់បង់ទិន្នន័យរបស់អ្នក ដែលអាចបណ្តាលមកពីការប្រើប្រាស់កម្មវិធីនោះ។"
+ "បន្ត"
+ "ការកំណត់"
+ "ការដំឡើង/ការលុបកម្មវិធីឧបករណ៍ពាក់"
+
diff --git a/packages/PackageInstaller/res/values-kn-television/strings.xml b/packages/PackageInstaller/res/values-kn-television/strings.xml
new file mode 100644
index 0000000000000..2af5a4bcb8b8f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ನಿರಾಕರಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಕೇಳಬೇಡ"
+ "ನೀವು ಇದನ್ನು ನಂತರದಲ್ಲಿ ಸೆಟ್ಟಿಂಗ್ಗಳು > ಅಪ್ಲಿಕೇಶನ್ಗಳಲ್ಲಿ ಬದಲಾಯಿಸಬಹುದು"
+ "%1$s / %2$s"
+ "ಸಿಸ್ಟಂ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ತೋರಿಸು"
+ "ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"
+ "ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"
+ "%1$s ಅನುಮತಿಗಳು"
+ "ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳು"
+ "%1$s ಅನುಮತಿಗಳು"
+
diff --git a/packages/PackageInstaller/res/values-kn-watch/strings.xml b/packages/PackageInstaller/res/values-kn-watch/strings.xml
new file mode 100644
index 0000000000000..1a9a9939e4d88
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ನಿರಾಕರಿಸಿ, ಮತ್ತೆ ಕೇಳಬೇಡಿ"
+ "%1$s / %2$s"
+ "ಸಿಸ್ಟಂ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ತೋರಿಸು"
+ "ಬದಲಾಯಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
+ "ಹೌದು"
+ "ರದ್ದುಮಾಡಿ"
+
diff --git a/packages/PackageInstaller/res/values-kn/strings.xml b/packages/PackageInstaller/res/values-kn/strings.xml
new file mode 100644
index 0000000000000..f42035c8bcdab
--- /dev/null
+++ b/packages/PackageInstaller/res/values-kn/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ಪ್ಯಾಕೇಜ್ ಸ್ಥಾಪಕ"
+ "ಮುಂದೆ"
+ "ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"
+ "ಮುಗಿದಿದೆ"
+ "ರದ್ದುಮಾಡಿ"
+ "ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"
+ "%1$s ಅನ್ನು ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"
+ "ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲಾಗಿದೆ."
+ "ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಇದು ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"
+ "ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."
+ "ನೀವು ಈ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಪ್ಲಿಕೇಶನ್ನ ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"
+ "ನೀವು ಈ ಬಿಲ್ಟ್-ಇನ್-ಅಪ್ಲಿಕೇಶನ್ನ ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಿಕೊಳ್ಳಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಇಲ್ಲಿಗೆ ಪ್ರವೇಶ ಪಡೆದುಕೊಳ್ಳುತ್ತದೆ:"
+ "ನೀವು ಈ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಅಪ್ಲಿಕೇಶನ್ನ ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."
+ "ನೀವು ಈ ಬಿಲ್ಟ್-ಇನ್-ಅಪ್ಲಿಕೇಶನ್ನ ಅಪ್ಡೇಟ್ ಆದ ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಿಕೊಳ್ಳಲು ಬಯಸುವಿರಾ? ಈಗಿರುವ ನಿಮ್ಮ ಡೇಟಾ ಕಳೆದು ಹೋಗುವುದಿಲ್ಲ. ಇದಕ್ಕೆ ಯಾವುದೇ ವಿಶೇಷ ಪ್ರವೇಶದ ಅಗತ್ಯವಿಲ್ಲ."
+ "ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲಾಗಿಲ್ಲ."
+ "ಸ್ಥಾಪಿಸುವಿಕೆಯಿಂದ ಪ್ಯಾಕೇಜ್ ಅನ್ನು ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."
+ "ಪ್ಯಾಕೇಜ್ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ತಿತ್ವದಲ್ಲಿರುವ ಪ್ಯಾಕೇಜ್ ಜೊತೆಗೆ ಸಂಘರ್ಷವಾಗುತ್ತದೆ."
+ "ಅಪ್ಲಿಕೇಶನ್ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಟಿವಿ ಜೊತೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."
+ "ಅಪ್ಲಿಕೇಶನ್ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಫೋನ್ ಜೊತೆಗೆ ಹೊಂದಾಣಿಕೆಯಾಗುವುದಿಲ್ಲ."
+ "ಪ್ಯಾಕೇಜ್ನಂತೆ ಸ್ಥಾಪಿತವಾಗದಿರುವ ಅಪ್ಲಿಕೇಶನ್ ಅಮಾನ್ಯವಾಗಿರುವಂತೆ ತೋರುತ್ತದೆ."
+ "ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ನಲ್ಲಿ %1$s ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."
+ "%1$s ಅನ್ನು ನಿಮ್ಮ ಟಿವಿಗೆ ಸ್ಥಾಪಿಸಲಾಗುವುದಿಲ್ಲ."
+ "ನಿಮ್ಮ ಫೋನ್ನಲ್ಲಿ %1$s ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗುವುದಿಲ್ಲ."
+ "ತೆರೆ"
+ "ನಿಮ್ಮ ನಿರ್ವಾಹಕರು ಅಪರಿಚಿತ ಮೂಲಗಳ ಅಪ್ಲಿಕೇಶನ್ಗಳ ಸ್ಥಾಪನೆಯನ್ನು ಅನುಮತಿಸುವುದಿಲ್ಲ"
+ "ಈ ಬಳಕೆದಾರರು ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
+ "ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ಈ ಬಳಕೆದಾರರನ್ನು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"
+ "ಸರಿ"
+ "ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"
+ "ಖಾಲಿ ಇಲ್ಲ"
+ "%1$s ಅನ್ನು ಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ. ಕೊಂಚ ಸ್ಥಳವನ್ನು ಖಾಲಿ ಮಾಡಿ. ಮತ್ತೆ ಪ್ರಯತ್ನಿಸಿ."
+ "ಅಪ್ಲಿಕೇಶನ್ ಕಂಡುಬಂದಿಲ್ಲ"
+ "ಸ್ಥಾಪಿಸಲಾಗಿರುವ ಅಪ್ಲಿಕೇಶನ್ಗಳ ಪಟ್ಟಿಯಲ್ಲಿ ಅಪ್ಲಿಕೇಶನ್ ಕಂಡುಬಂದಿಲ್ಲ."
+ "ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ"
+ "ಈ ಅಸ್ಥಾಪಿಸುವಿಕೆಯನ್ನು ಪ್ರಸ್ತುತ ಬಳಕೆದಾರರಿಗೆ ನಿರ್ವಹಿಸಲು ಅನುಮತಿಸಲಾಗುವುದಿಲ್ಲ."
+ "ದೋಷ"
+ "ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಾಗಲಿಲ್ಲ."
+ "ಅಪ್ಲಿಕೇಶನ್ ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"
+ "ನವೀಕರಣವನ್ನು ಅನ್ಇನ್ಸ್ಟಾಲ್ ಮಾಡಿ"
+ "%1$s ಎಂಬುದು ಕೆಳಗಿನ ಅಪ್ಲಿಕೇಶನ್ನ ಭಾಗವಾಗಿದೆ:"
+ "ನೀವು ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ?"
+ "ನೀವು ""ಎಲ್ಲಾ"" ಬಳಕೆದಾರರಿಗೂ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಬಯಸುವಿರಾ? ಸಾಧನದಲ್ಲಿನ ""ಎಲ್ಲಾ"" ಬಳಕೆದಾರರಿಂದ ಅಪ್ಲಿಕೇಶನ್ ಮತ್ತು ಅದರ ಡೇಟಾವನ್ನು ತೆಗೆದುಹಾಕಲಾಗುವುದು."
+ "%1$s ಬಳಕೆದಾರರಿಗೆ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ನೀವು ಬಯಸುವಿರಾ?"
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ."
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ ಬದಲಿಗೆ ಫ್ಯಾಕ್ಟರಿ ಆವೃತ್ತಿಯನ್ನು ಬದಲಾಯಿಸುವುದೇ? ಎಲ್ಲಾ ಡೇಟಾ ತೆಗೆದುಹಾಕಲಾಗುತ್ತದೆ. ಕೆಲಸದ ಪ್ರೊಫೈಲ್ಗಳನ್ನು ಹೊಂದಿರುವವುಗಳನ್ನು ಒಳಗೊಂಡಂತೆ ಈ ಸಾಧನದ ಎಲ್ಲಾ ಬಳಕೆದಾರರಿಗೆ ಇದು ಪರಿಣಾಮ ಬೀರುತ್ತದೆ."
+ "ಚಾಲನೆಯಲ್ಲಿರುವ ಅಸ್ಥಾಪನೆಗಳು"
+ "ವಿಫಲಗೊಂಡ ಅಸ್ಥಾಪನೆಗಳು"
+ "ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"
+ "%1$s ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ…"
+ "ಅನ್ಇನ್ಸ್ಟಾಲ್ ಪೂರ್ಣಗೊಂಡಿದೆ."
+ "%1$s ಅಸ್ಥಾಪಿಸಲಾಗಿದೆ"
+ "ಅನ್ಇನ್ಸ್ಟಾಲ್ ವಿಫಲವಾಗಿದೆ."
+ "%1$s ಅಸ್ಥಾಪಿಸುವಿಕೆ ಯಶಸ್ವಿಯಾಗಲಿಲ್ಲ."
+ "ಸಕ್ರಿಯ ಸಾಧನ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
+ "%1$s ಗಾಗಿ ಸಕ್ರಿಯ ಸಾಧನ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ"
+ "ಕೆಲವು ಬಳಕೆದಾರರು ಅಥವಾ ಪ್ರೊಫೈಲ್ಗಳಿಗೆ ಈ ಅಪ್ಲಿಕೇಶನ್ ಅಗತ್ಯವಿರುತ್ತದೆ ಮತ್ತು ಇತರರಿಗೆ ಅಸ್ಥಾಪಿಸಲಾಗಿದೆ"
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ನಿಮ್ಮ ಪ್ರೊಫೈಲ್ನ ಅಗತ್ಯವಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ ನಿಮ್ಮ ಸಾಧನ ನಿರ್ವಾಹಕರಿಗೆ ಅಗತ್ಯವಿದೆ ಮತ್ತು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
+ "ಸಾಧನದ ನಿರ್ವಹಣೆ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿರ್ವಹಿಸಿ"
+ "ಬಳಕೆದಾರರನ್ನು ನಿರ್ವಹಿಸಿ"
+ "%1$s ಅನ್ನು ಅಸ್ಥಾಪಿಸಲು ಸಾಧ್ಯವಿಲ್ಲ."
+ "ಪ್ಯಾಕೇಜ್ ಪಾರ್ಸ್ ಮಾಡುವಲ್ಲಿ ಸಮಸ್ಯೆ ಕಂಡುಬಂದಿದೆ."
+ "ಹೊಸತು"
+ "ಎಲ್ಲಾ"
+ "ಗೌಪ್ಯತೆ"
+ "ಸಾಧನ ಪ್ರವೇಶ"
+ "ಈ ನವೀಕರಣಕ್ಕೆ ಯಾವುದೇ ಹೊಸ ಅನುಮತಿಗಳ ಅಗತ್ಯವಿಲ್ಲ."
+ "ನಿರಾಕರಿಸಿ"
+ "ಇನ್ನಷ್ಟು ಮಾಹಿತಿ"
+ "ಹೇಗಾದರೂ ನಿರಾಕರಿಸಿ"
+ "%2$s ರಲ್ಲಿ %1$s"
+ "%2$s%1$s ಅನ್ನು ಅನುಮತಿಸುವುದೇ?"
+ "<b>%1$s</b> to %2$s ಅನ್ನು ಯಾವಾಗಲೂ ಅನುಮತಿಸುವುದೇ?"
+ "ಅಪ್ಲಿಕೇಶನ್ ಬಳಸುವಾಗ ಮಾತ್ರ"
+ "ಯಾವಾಗಲೂ"
+ "ನಿರಾಕರಿಸಿ ಹಾಗೂ ಮತ್ತೊಮ್ಮೆ ಕೇಳಬೇಡ"
+ "%1$d ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+ "ಎಲ್ಲಾ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+ "ಯಾವುದನ್ನೂ ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿಲ್ಲ"
+ "ಅನುಮತಿಸಿ"
+ "ಅಪ್ಲಿಕೇಶನ್ಗಳು"
+ "ಅಪ್ಲಿಕೇಶನ್ ಅನುಮತಿಗಳು"
+ "ಮತ್ತೆ ಕೇಳಬೇಡಿ"
+ "ಯಾವುದೇ ಅನುಮತಿಗಳಿಲ್ಲ"
+ "ಹೆಚ್ಚುವರಿ ಅನುಮತಿಗಳು"
+ "ಅಪ್ಲಿಕೇಶನ್ ಮಾಹಿತಿಯನ್ನು ತೆರೆಯಿರಿ"
+
+ %1$d ಇನ್ನಷ್ಟು
+ %1$d ಇನ್ನಷ್ಟು
+
+ "ಈ ಅಪ್ಲಿಕೇಶನ್ Android ನ ಹಳೆಯ ಆವೃತ್ತಿಗೆ ವಿನ್ಯಾಸಗೊಳಿಸಲಾಗಿತ್ತು. ಅನುಮತಿ ನಿರಾಕರಿಸುವಿಕೆ ಇನ್ನು ಮುಂದೆ ಉದ್ದೇಶಿಸಲ್ಪಟ್ಟಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸದೆ ಇರುವುದಕ್ಕೆ ಇದು ಕಾರಣವಾಗಬಹುದು."
+ "ಅಪರಿಚಿತ ಕ್ರಿಯೆಯನ್ನು ಮಾಡಿ"
+ "%2$d ರಲ್ಲಿ %1$d ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಅನುಮತಿಸಲಾಗಿದೆ"
+ "ಸಿಸ್ಟಂ ತೋರಿಸು"
+ "ಸಿಸ್ಟಂ ಮರೆಮಾಡು"
+ "ಅಪ್ಲಿಕೇಶನ್ಗಳು ಇಲ್ಲ"
+ "ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್ಗಳು"
+ "%1$s ಅಪ್ಲಿಕೇಶನ್ ಈ ಸಾಧನಕ್ಕೆ ಸ್ಥಳ ಸೇವೆಗಳನ್ನು ಒದಗಿಸುತ್ತದೆ. ಸ್ಥಳ ಪ್ರವೇಶವನ್ನು ಸ್ಥಳ ಸೆಟ್ಟಿಂಗ್ಗಳಿಂದ ಮಾರ್ಪಡಿಸಬಹುದು."
+ "ನೀವು ಈ ಅನುಮತಿಯನ್ನು ನಿರಾಕರಿಸಿದರೆ, ಇನ್ನು ಮುಂದೆ ನಿಮ್ಮ ಸಾಧನದ ಮೂಲ ವೈಶಿಷ್ಟ್ಯಗಳು ಉದ್ದೇಶಿದಂತೆ ಕಾರ್ಯನಿರ್ವಹಿಸದಿರಬಹುದು."
+ "ನೀತಿಯ ಮೂಲಕ ಜಾರಿಗೊಳಿಸಲಾಗಿದೆ"
+ "ನೀತಿಯ ಮೂಲಕ ಹಿನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ನಿಷ್ಕ್ರಿಯಗೊಳಿಸಲಾಗಿದೆ"
+ "ನೀತಿ ಮೂಲಕ ಹಿನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾದ"
+ "ನೀತಿ ಮೂಲಕ ಮುನ್ನೆಲೆ ಪ್ರವೇಶವನ್ನು ಸಕ್ರಿಯಗೊಳಿಸಲಾದ"
+ "ನಿರ್ವಾಹಕರ ಮೂಲಕ ನಿಯಂತ್ರಿಸಲಾಗಿದೆ"
+
+
+
+ "ಯಾವಾಗಲೂ"
+ "ಅಪ್ಲಿಕೇಶನ್ ಬಳಸುವಾಗ ಮಾತ್ರ"
+ "ಎಂದೂ ಇಲ್ಲ"
+ "ಲೋಡ್ ಆಗುತ್ತಿದೆ..."
+ "ಎಲ್ಲ ಅನುಮತಿಗಳು"
+ "ಇತರ ಅಪ್ಲಿಕೇಶನ್ ಸಾಮರ್ಥ್ಯಗಳು"
+ "ಅನುಮತಿ ವಿನಂತಿ"
+ "ಪರದೆ ಆವರಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಪತ್ತೆಹಚ್ಚಲಾಗಿದೆ"
+ "ಈ ಅನುಮತಿ ಸೆಟ್ಟಿಂಗ್ ಬದಲಾಯಿಸಲು, ನೀವು ಮೊದಲು ಸೆಟ್ಟಿಂಗ್ಗಳು > ಅಪ್ಲಿಕೇಶನ್ಗಳಿಂದ ಪರದೆ ಆವರಿಸಿಕೊಳ್ಳುವಿಕೆಯನ್ನು ಆಫ್ ಮಾಡಬೇಕು"
+ "ಸೆಟ್ಟಿಂಗ್ಗಳನ್ನು ತೆರೆಯಿರಿ"
+ "Android Wear"
+ "Wear ನಲ್ಲಿ ಸ್ಥಾಪಿಸುವಿಕೆ/ಅಸ್ಥಾಪಿಸುವಿಕೆ ಕ್ರಿಯೆಗಳು ಬೆಂಬಲಿತವಾಗಿಲ್ಲ."
+ "<b>%1$s</b> ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆರಿಸಿಕೊಳ್ಳಿ"
+ "<b>%1$s</b> ಅನ್ನು ಅಪ್ಡೇಟ್ ಮಾಡಲಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ಗೆ ಪ್ರವೇಶಿಸಲು ಯಾವುದನ್ನು ಅನುಮತಿಸಬೇಕು ಎಂಬುದನ್ನು ಆರಿಸಿಕೊಳ್ಳಿ."
+ "ರದ್ದುಮಾಡಿ"
+ "ಮುಂದುವರಿಸು"
+ "ಹೊಸ ಅನುಮತಿಗಳು"
+ "ಪ್ರಸ್ತುತ ಅನುಮತಿಗಳು"
+ "ಸ್ಥಾಪಿಸಲು ಸಿದ್ಧವಿರುವ ಅಪ್ಲಿಕೇಶನ್…"
+ "ಅಪರಿಚಿತ"
+ "ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಈ ಮೂಲದಿಂದ ಬಂದಿರುವ ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ಗೆ ಅನುಮತಿಯಿಲ್ಲ."
+ "ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಅಪರಿಚಿತ ಮೂಲಗಳಿಂದ ಪಡೆದುಕೊಳ್ಳುವ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ನಿಮ್ಮ ಟಿವಿಯು ಸ್ಥಾಪಿಸದಂತೆ ನಿರ್ಬಂಧಿಸಲಾಗಿದೆ."
+ "ನಿಮ್ಮ ಸುರಕ್ಷತೆಯ ದೃಷ್ಟಿಯಿಂದ, ಈ ಮೂಲದಿಂದ ಬಂದಿರುವ ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳನ್ನು ಸ್ಥಾಪಿಸಲು ನಿಮ್ಮ ಫೋನ್ಗೆ ಅನುಮತಿಯಿಲ್ಲ."
+ "ನಿಮ್ಮ ಫೋನ್ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್ಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."
+ "ನಿಮ್ಮ ಟ್ಯಾಬ್ಲೆಟ್ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಫೋನ್ಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."
+ "ನಿಮ್ಮ ಟಿವಿ ಹಾಗೂ ವೈಯಕ್ತಿಕ ಡೇಟಾ, ಅಪರಿಚಿತ ಅಪ್ಲಿಕೇಶನ್ಗಳ ದಾಳಿಗೆ ತುತ್ತಾಗುವ ಸಾಧ್ಯತೆ ಹೆಚ್ಚಾಗಿದೆ. ಈ ಅಪ್ಲಿಕೇಶನ್ ಅನ್ನು ಸ್ಥಾಪಿಸುವ ಮೂಲಕ, ನಿಮ್ಮ ಟಿವಿಗೆ ಯಾವುದೇ ಹಾನಿ ಉಂಟಾದರೆ ಅಥವಾ ಅದರ ಬಳಕೆಯಿಂದ ಡೇಟಾ ನಷ್ಟವಾದರೆ, ಅದಕ್ಕೆ ನೀವೇ ಜವಾಬ್ದಾರರು ಎನ್ನುವುದನ್ನು ಒಪ್ಪಿಕೊಳ್ಳುತ್ತೀರಿ."
+ "ಮುಂದುವರಿಸಿ"
+ "ಸೆಟ್ಟಿಂಗ್ಗಳು"
+ "wear ಅಪ್ಲಿಕೇಶನ್ ಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ/ಅಸ್ಥಾಪಿಸಲಾಗುತ್ತಿದೆ"
+
diff --git a/packages/PackageInstaller/res/values-ko-television/strings.xml b/packages/PackageInstaller/res/values-ko-television/strings.xml
new file mode 100644
index 0000000000000..6ad2ed6c21ac1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "거부 및 다시 묻지 않음"
+ "설정 > 앱에서 나중에 변경할 수 있습니다."
+ "%2$s개 중 %1$s"
+ "시스템 앱 보기"
+ "앱 권한"
+ "앱 권한"
+ "%1$s 권한"
+ "추가 권한"
+ "%1$s 권한"
+
diff --git a/packages/PackageInstaller/res/values-ko-watch/strings.xml b/packages/PackageInstaller/res/values-ko-watch/strings.xml
new file mode 100644
index 0000000000000..6cce6f94a5f64
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "거부 및 다시 묻지 않음"
+ "%1$s/%2$s"
+ "시스템 앱 보기"
+ "변경할 수 없음"
+ "예"
+ "취소"
+
diff --git a/packages/PackageInstaller/res/values-ko/strings.xml b/packages/PackageInstaller/res/values-ko/strings.xml
new file mode 100644
index 0000000000000..235a1d4a41d78
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ko/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "패키지 설치 프로그램"
+ "다음"
+ "설치"
+ "완료"
+ "취소"
+ "설치 중..."
+ "%1$s 설치 중…"
+ "앱이 설치되었습니다."
+ "이 애플리케이션을 설치하시겠습니까? 애플리케이션이 다음에 액세스할 수 있습니다."
+ "이 애플리케이션을 설치하시겠습니까? 특별한 액세스 권한이 필요하지 않습니다."
+ "기존 애플리케이션에 업데이트를 설치하시겠습니까? 기존 데이터는 손실되지 않습니다. 업데이트된 애플리케이션이 다음에 액세스할 수 있습니다."
+ "내장 애플리케이션에 업데이트를 설치하시겠습니까? 기존 데이터는 손실되지 않습니다. 업데이트된 애플리케이션이 다음에 액세스할 수 있습니다."
+ "기존의 애플리케이션 업데이트를 설치하시겠습니까? 기존의 데이터는 손실되지 않으며 특별한 액세스 권한이 필요하지 않습니다."
+ "내장 애플리케이션 업데이트를 설치하시겠습니까? 기존의 데이터는 손실되지 않으며 특별한 액세스 권한이 필요하지 않습니다."
+ "앱이 설치되지 않았습니다."
+ "패키지 설치가 차단되었습니다."
+ "패키지가 기존 패키지와 충돌하여 앱이 설치되지 않았습니다."
+ "앱이 태블릿과 호환되지 않아서 설치되지 않았습니다."
+ "앱이 사용 중인 TV와 호환되지 않습니다."
+ "앱이 휴대전화와 호환되지 않아서 설치되지 않았습니다."
+ "패키지가 잘못되어 앱이 설치되지 않았습니다."
+ "태블릿에 %1$s 앱을 설치할 수 없습니다."
+ "%1$s을(를) TV에 설치할 수 없습니다."
+ "휴대전화에 %1$s 앱을 설치할 수 없습니다."
+ "열기"
+ "관리자가 알 수 없는 출처의 앱 설치를 허용하지 않습니다."
+ "이 사용자는 알 수 없는 앱을 설치할 수 없습니다."
+ "이 사용자는 앱을 설치할 권한이 없습니다."
+ "확인"
+ "앱 관리"
+ "여유 공간이 없음"
+ "%1$s 앱을 설치할 수 없습니다. 여유 공간을 늘린 후에 다시 시도하세요."
+ "앱을 찾을 수 없음"
+ "설치된 앱 목록에 앱이 없습니다."
+ "허용되지 않음"
+ "현재 사용자는 이 제거를 수행할 수 없습니다."
+ "오류"
+ "앱을 제거할 수 없습니다."
+ "앱 제거"
+ "업데이트 제거"
+ "%1$s은(는) 다음 앱의 일부입니다."
+ "이 앱을 제거하시겠습니까?"
+ "모든"" 사용자에 대해 이 앱을 제거하시겠습니까? 기기를 사용하는 ""모든"" 사용자에 대해 애플리케이션 및 데이터가 삭제됩니다."
+ "%1$s님의 기기에 설치된 앱을 제거하시겠습니까?"
+ "이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제됩니다."
+ "이 앱을 초기 버전으로 바꾸시겠습니까? 모든 데이터가 삭제되며 직장 프로필 사용자를 포함해 이 기기의 모든 사용자에게 영향을 미칩니다."
+ "실행 중인 제거 작업"
+ "실패한 제거 작업"
+ "제거 중..."
+ "%1$s 제거 중…"
+ "제거를 완료했습니다."
+ "%1$s을(를) 제거했습니다."
+ "제거하지 못했습니다."
+ "%1$s을(를) 제거하지 못했습니다."
+ "활성 상태의 기기 관리자 앱을 제거할 수 없습니다."
+ "%1$s의 활성 상태의 기기 관리자 앱을 제거할 수 없습니다."
+ "이 앱은 일부 사용자 또는 프로필에 필요하며 다른 사용자에 대해서는 제거되었습니다."
+ "이 앱은 프로필에 필요하므로 삭제할 수 없습니다."
+ "이 앱은 기기 관리자에게 필요하므로 제거할 수 없습니다."
+ "기기 관리자 앱 관리"
+ "사용자 관리"
+ "%1$s 앱을 제거할 수 없습니다."
+ "패키지를 파싱하는 중 문제가 발생했습니다."
+ "새 권한"
+ "전체"
+ "개인정보 보호"
+ "기기 액세스"
+ "이 업데이트에는 새로운 권한이 필요하지 않습니다."
+ "거부"
+ "추가 정보"
+ "거부"
+ "%1$s/%2$s"
+ "<b>%1$s</b>의 다음 작업을 허용하시겠습니까? %2$s"
+ "<b>%1$s</b>에서 %2$s하도록 허용하시겠습니까??"
+ "앱 사용 중에만"
+ "항상"
+ "거부 및 다시 묻지 않음"
+ "%1$d개 중지됨"
+ "모두 중지됨"
+ "아무것도 중지되지 않음"
+ "허용"
+ "앱"
+ "앱 권한"
+ "다시 묻지 않음"
+ "권한이 없음"
+ "추가 권한"
+ "앱 정보 열기"
+
+ %1$d개 더보기
+ %1$d개 더보기
+
+ "이 앱은 Android 이전 버전에 맞게 설계되었습니다. 권한을 거부하면 정상적으로 작동하지 않을 수 있습니다."
+ "알 수 없는 작업 수행"
+ "%1$d/%2$d개 앱 허용됨"
+ "시스템 표시"
+ "시스템 숨기기"
+ "앱 없음"
+ "위치 설정"
+ "이 기기의 위치 서비스 제공업체는 %1$s입니다. 위치 정보 액세스는 위치 설정에서 수정할 수 있습니다."
+ "이 권한을 거부하는 경우 기기의 기본 기능이 제대로 작동하지 않을 수 있습니다."
+ "정책에 의해 시행됨"
+ "정책에 따라 백그라운드 액세스 사용 중지됨"
+ "정책에 따라 백그라운드 액세스 사용 설정함"
+ "정책에 따라 포그라운드 액세스 사용 설정함"
+ "관리자가 제어"
+
+
+
+ "항상"
+ "앱 사용 중에만"
+ "사용 안함"
+ "로드 중..."
+ "모든 권한"
+ "다른 앱 기능"
+ "권한 요청"
+ "화면 오버레이 감지됨"
+ "이 권한 설정을 변경하려면 먼저 설정 > 앱에서 화면 오버레이를 사용하지 않도록 설정해야 합니다."
+ "설정 열기"
+ "Android Wear"
+ "Wear에서 지원하지 않는 설치/제거 작업입니다."
+ "<b>%1$s</b>에서 액세스하도록 허용할 항목을 선택하세요."
+ "<b>%1$s</b>이(가) 업데이트되었습니다. 이 앱에서 액세스하도록 허용할 항목을 선택하세요."
+ "취소"
+ "계속"
+ "새로운 권한"
+ "기존 권한"
+ "앱 준비 중…"
+ "알 수 없음"
+ "보안상의 이유로 이 경로를 통한 알 수 없는 앱을 태블릿에 설치할 수 없습니다."
+ "보안상의 이유로 이 경로를 통한 알 수 없는 앱을 TV에 설치할 수 없습니다."
+ "보안상의 이유로 이 경로를 통한 알 수 없는 앱을 휴대전화에 설치할 수 없습니다."
+ "휴대전화와 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 휴대전화 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."
+ "태블릿과 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 태블릿 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."
+ "TV와 개인 데이터는 알 수 없는 앱의 공격에 더욱 취약합니다. 이 앱을 설치하면 앱 사용으로 인해 발생할 수 있는 모든 TV 손상이나 데이터 손실에 사용자가 책임을 진다는 것에 동의하게 됩니다."
+ "계속"
+ "설정"
+ "Wear 앱 설치/제거"
+
diff --git a/packages/PackageInstaller/res/values-ky-television/strings.xml b/packages/PackageInstaller/res/values-ky-television/strings.xml
new file mode 100644
index 0000000000000..20994ffb2bf31
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Баш тартам жана экинчи суралбасын"
+ "Муну кийин Жөндөөлөр > Колдонмолордон өзгөртө аласыз"
+ "%1$s / %2$s"
+ "Тутум колдонмолорун көрсөтүү"
+ "Колдонмонун уруксаттары"
+ "Колдонмонун уруксаттары"
+ "%1$s уруксаттары"
+ "Кошумча уруксаттар"
+ "%1$s уруксаттары"
+
diff --git a/packages/PackageInstaller/res/values-ky-watch/strings.xml b/packages/PackageInstaller/res/values-ky-watch/strings.xml
new file mode 100644
index 0000000000000..aadb7c4938121
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Баш тарам, экинчи суралбасын"
+ "%1$s / %2$s"
+ "Тутум колдонмолорун көрсөтүү"
+ "Өзгөртүүгө болбойт"
+ "Ооба"
+ "Жокко чыгаруу"
+
diff --git a/packages/PackageInstaller/res/values-ky/strings.xml b/packages/PackageInstaller/res/values-ky/strings.xml
new file mode 100644
index 0000000000000..9a95c54210328
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ky/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Топтом орноткуч"
+ "Кийинки"
+ "Орнотуу"
+ "Даяр"
+ "Жокко чыгаруу"
+ "Орнотулууда…"
+ "%1$s орнотулууда…"
+ "Колдонмо орнотулду."
+ "Бул колдонмону орнотоюн дегениңиз аныкпы? Ал кийинкилерге жетки алат:"
+ "Бул колдонмону орнотоюн дегениңиз аныкпы? Ал эч бир атайын жетки уруксаттарын талап кылбайт."
+ "Учурда иштеп турган бул колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Жаңыртылган колдонмо кийинкилерге жетки алат:"
+ "Бул камтылган колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Жаңыртылган колдонмо кийинкилерге жетки алат:"
+ "Учурда иштпе турган бул колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Ал эч бир атайын жетки уруксаттарын талап кылбайт."
+ "Бул камтылган колдонмого жаңыртуу орнотоюн дегениңиз аныкпы? Сиздин сакталган берилиштериңиз өчүрүлбөйт. Ал эч бир атайын жетки уруксаттарын талап кылбайт."
+ "Колдонмо орнотулган жок."
+ "Топтом орнотуудан бөгөттөлгөн."
+ "Башка топтом менен дал келбегендиктен колдонмо орнотулган жок."
+ "Бул колдонмо планшетиңизге шайкеш эмес болгондуктан колдонмо орнотулган жок."
+ "Бул колдонмо сыналгыңызга шайкеш келбейт."
+ "Бул колдонмо телефонуңузга шайкеш эмес болгондуктан колдонмо орнотулган жок."
+ "Топтом катары орнотулбай калган колдонмо жараксыз окшойт."
+ "%1$s колдонмосун планшетиңизге орнотуу мүмкүн эмес."
+ "%1$s сыналгыңызга орнотулбай койду."
+ "%1$s колдонмосун телефонуңузга орнотуу мүмкүн эмес."
+ "Ачуу"
+ "Администраторуңуз белгисиз булактардан алынган колдонмолордун орнотулушуна жол бербейт"
+ "Бул колдонуучу белгисиз колдонмолорду орното албайт"
+ "Бул колдонуучу колдонмолорду орното албайт"
+ "Жарайт"
+ "Колдонмолорду башкаруу"
+ "Бош орун жок"
+ "%1$s колдонмосун телефонуңузга орнотуу мүмкүн эмес. Орун бошотуп, кайталап орнотуп көрүңүз."
+ "Колдонмо табылган жок"
+ "Колдонмо орнотулган колдонмолор тизмегинен табылган жок."
+ "Тыюу салынган"
+ "Учурдагы колдонуучу колдонмону чыгарып сала албайт."
+ "Ката"
+ "Колдонмону чыгарып салуу мүмкүн болбой жатат."
+ "Колдонмону чечип салуу"
+ "Жаңыртууну чечип салуу"
+ "%1$s кийинки колдонмонун бөлүгү:"
+ "Бул колдонмону чечип салгыңыз келеби?"
+ "Бул колдонмо ""бардык"" колдонуучулардан алынып салынсынбы? Бул колдонмо жана анын берилиштери бул түзмөктүн ""бардык"" колдонуучуларынан алынат."
+ "Бул колдонмону %1$s колдонуучусу үчүн орнотуудан чыгаргыңыз келеби?"
+ "Бул колдонмонун баштапкы версиясы орнотулсунбу? Бардык дайындар өчүп калат."
+ "Бул колдонмонун баштапкы версиясы орнотулсунбу? Түзмөктөгү бардык профилдердин, ошондой эле жумушчу профилдин дайындары өчүп калат."
+ "Чыгарылып салынууда"
+ "Чыгарылып салынбай калгандар"
+ "Чыгарылып салынууда…"
+ "%1$s колдонмосу чыгарылууда…"
+ "Чечилип бүттү."
+ "%1$s колдонмосу чыгарылды"
+ "Чечүү ийгиликсиз болду."
+ "%1$s колдонмосун чыгарып салуу ишке ашкан жок."
+ "Түзмөктү башкарган колдонмо иштеп жатканда аны чыгарып салууга болбойт"
+ "%1$s колдонуучусунун түзмөктү башкарган колдонмосу иштеп жатканда, аны чыгарып салууга болбойт"
+ "Колдонмо айрым колдонуучулар же профилдерге керек."
+ "Бул колдонмо профилиңизге керек жана аны чыгарып салуу мүмкүн эмес."
+ "Бул колдонмо түзмөк администраторуңузга керектелет жана орнотуудан чыгаруу мүмкүн эмес."
+ "Түзмөктү башкарган колдонмолорду көзөмөлдөө"
+ "Колдонуучуларды башкаруу"
+ "%1$s колдонмосун чечип салуу мүмкүн эмес."
+ "Таңгакты окууда маселе пайда болду."
+ "Жаңы"
+ "Бардыгы"
+ "Купуялуулук"
+ "Жетүү уруксаттары"
+ "Бул жаңыртуу жаңы уруксаттарды талап кылбайт."
+ "Жок"
+ "Көбүрөөк маалымат"
+ "Баш тартуу"
+ "%2$s ичинен %1$s"
+ "<b>%1$s</b> колдонмосуна %2$s уруксат берилсинби?"
+ "<b>%1$s</b> колдонмосуна %2$s аракетине ар дайым уруксат берилсинби?"
+ "Ушул колдонмодо иштегенде гана"
+ "Ар дайым"
+ "Баш тартам жана экинчи суралбасын"
+ "%1$d өчүрүлгөн"
+ "баары өчүрүлгөн"
+ "эч бири өчүрүлгөн жок"
+ "Уруксат берүү"
+ "Колдонмолор"
+ "Колдонмо уруксаттары"
+ "Экинчи суралбасын"
+ "Уруксаттар жок"
+ "Кошумча уруксаттар"
+ "Колдонмо тууралуу маалыматты ачуу"
+
+ Дагы %1$d
+ Дагы %1$d
+
+ "Бул колдонмо эски Android версиясы үчүн түзүлгөн. Уруксат берилбесе, ал туура эмес иштеп калышы мүмкүн."
+ "белгисиз аракеттерди жасайт"
+ "%2$d колдонмонун ичинен %1$d уруксат берилген"
+ "Тутумду көрсөтүү"
+ "Тутумдагы процесстерди жашыруу"
+ "Бир дагы колдонмо жок"
+ "Геолокация параметрлери"
+ "%1$s - бул түзмөктөгү жайгашкан жерди аныктоо кызматынын камсыздоочусу. Жайгашкан жерди көрүү мүмкүнчүлүгүн жайгашкан жерди аныктоо жөндөөлөрүнөн өзгөртсө болот."
+ "Эгер бул уруксатты четке каксаңыз, түзмөгүңүздүн негизги функциялары талаптагыдай иштебей калышы мүмкүн."
+ "Саясат тарабынан күчүнө киргизилген"
+ "Фондук режимде колдонуу саясат тарабынан өчүрүлгөн"
+ "Фондук режимде колдонуу саясат тарабынан иштетилген"
+ "Активдүү режим саясат боюнча иштетилген"
+ "Администратор тарабынан көзөмөлдөнөт"
+
+
+
+ "Ар дайым"
+ "Ушул колдонмодо иштегенде гана"
+ "Эч качан"
+ "Жүктөлүүдө…"
+ "Бардык уруксаттар"
+ "Колдонмонун башка жөндөмдөрү"
+ "Уруксат суроо"
+ "Экран кабатталышы аныкталды"
+ "Бул уруксаттын жөндөөсүн өзгөртүү үчүн, адегенде Жөндөөлөр > Колдонмолордон экрандын кабатталышын өчүрүңүз"
+ "Жөндөөлөрдү ачуу"
+ "Android Тагынма"
+ "Орнотуу/чыгарып салуу аракеттери Android Wear\'де колдоого алынбайт."
+ "<b>%1$s</b> колдонмосу үчүн уруксаттарды тандаңыз"
+ "<b>%1$s</b> жаңыртылды. Ал үчүн уруксаттарды тандаңыз."
+ "Жок"
+ "Улантуу"
+ "Жаңы уруксаттар"
+ "Учурдагы уруксаттар"
+ "Күтө туруңуз…"
+ "Белгисиз"
+ "Коопсуздукту сактоо максатында, планшетиңизге бул булактан колдонмолорду орнотууга уруксат жок."
+ "Коопсуздукту сактоо максатында, сыналгыңызга бул булактан колдонмолорду орнотууга уруксат жок."
+ "Коопсуздукту сактоо максатында, телефонуңузга бул булактан колдонмолорду орнотууга уруксат жок."
+ "Телефонуңуз жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам телефонуңузга кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."
+ "Планшетиңиз жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам планшетиңизге кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."
+ "TV жана жеке дайындарыңыз белгисиз колдонмолордон зыян тартып калышы мүмкүн. Бул колдонмону орнотуп, аны пайдалануудан улам TV\'ңизге кандайдыр бир зыян келтирилсе же дайындарыңызды жоготуп алсаңыз, өзүңүз жооптуу болосуз."
+ "Улантуу"
+ "Жөндөөлөр"
+ "Тагынма колдонмолорду орнотуу/чыгаруу"
+
diff --git a/packages/PackageInstaller/res/values-lo-television/strings.xml b/packages/PackageInstaller/res/values-lo-television/strings.xml
new file mode 100644
index 0000000000000..a6f4e49cdcd4f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ປະຕິເສດ ແລະຢ່າຖາມອີກ"
+ "ທ່ານສາມາດປ່ຽນແປງສິ່ງນີ້ໃນພາຍຫຼັງໄດ້ໃນການຕັ້ງຄ່າ > ແອັບ"
+ "%1$s / %2$s"
+ "ສະແດງແອັບລະບົບ"
+ "ການອະນຸຍາດແອັບ"
+ "ການອະນຸຍາດແອັບ"
+ "%1$s ການອະນຸຍາດ"
+ "ການອະນຸຍາດເພີ່ມເຕີມ"
+ "%1$s ການອະນຸຍາດ"
+
diff --git a/packages/PackageInstaller/res/values-lo-watch/strings.xml b/packages/PackageInstaller/res/values-lo-watch/strings.xml
new file mode 100644
index 0000000000000..4fae329ce90a3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ປະຕິເສດ, ຢ່າຖາມອີກ"
+ "%1$s / %2$s"
+ "ສະແດງແອັບລະບົບ"
+ "ບໍ່ສາມາດປ່ຽນແປງໄດ້"
+ "ແມ່ນແລ້ວ"
+ "ຍົກເລີກ"
+
diff --git a/packages/PackageInstaller/res/values-lo/strings.xml b/packages/PackageInstaller/res/values-lo/strings.xml
new file mode 100644
index 0000000000000..f39f140a8ab5e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lo/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ໂຕຕິດຕັ້ງແພັກເກດ"
+ "ຕໍ່ໄປ"
+ "ຕິດຕັ້ງ"
+ "ແລ້ວໆ"
+ "ຍົກເລີກ"
+ "ກຳລັງຕິດຕັ້ງ…"
+ "ກຳລັງຕິດຕັ້ງ %1$s…"
+ "ຕິດຕັ້ງແອັບຯແລ້ວ."
+ "ທ່ານຕ້ອງການຕິດຕັ້ງແອັບພລິເຄຊັນນີ້ບໍ່? ມັນຈະໄດ້ສິດການເຂົ້າເຖິງ:"
+ "ທ່ານຕ້ອງການຕິດຕັ້ງແອັບພລິເຄຊັນນີ້ບໍ່? ມັນບໍ່ຕ້ອງໃຊ້ສິດທິການເຂົ້າເຖິງພິເສດໃດໆ."
+ "ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ແອັບພລິເຄຊັນທີ່ຜ່ານການອັບເດດຈະສາມາດເຂົ້າເຖິງ:"
+ "ທ່ານຕ້ອງການທີ່ຈະຕິດຕັ້ງຊຸດອັບເດດສຳລັບແອັບຯນີ້ບໍ່? ຂໍ້ມູນທີ່ທ່ານມີຢູ່ຈະບໍ່ສູນຫາຍ. ການອັບເດດແອັບພລິເຄຊັນນີ້ຈະສາມາດເຂົ້າເຖິງ:"
+ "ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ມັນບໍ່ຕ້ອງການສິດເຂົ້າເຖິງພິເສດໃດໆ."
+ "ທ່ານຕ້ອງການຕິດຕັ້ງອັບເດດໃສ່ແອັບພລິເຄຊັນທີ່ມີມານຳນີ້ບໍ່? ຂໍ້ມູນຂອງທ່ານທີ່ມີຢູ່ກ່ອນແລ້ວຈະຍັງຄົງຢູ່ຄືເກົ່າ. ມັນບໍ່ຕ້ອງການສິດເຂົ້າເຖິງພິເສດໃດໆເລີຍ."
+ "ບໍ່ໄດ້ຕິດຕັ້ງແອັບຯເທື່ອ."
+ "ແພັກເກດຖືກບລັອກບໍ່ໃຫ້ໄດ້ຮັບການຕິດຕັ້ງ"
+ "App not installed as package conflicts with an existing package."
+ "App not installed as app isn\'t compatible with your tablet."
+ "ແອັບນີ້ບໍ່ເຂົ້າກັນໄດ້ກັບໂທລະພາບຂອງທ່ານ."
+ "App not installed as app isn\'t compatible with your phone."
+ "App not installed as package appears to be invalid."
+ "ບໍ່ສາມາດຕິດຕັ້ງ %1$s ໃສ່ແທັບເລັດຂອງທ່ານໄດ້."
+ "%1$s ບໍ່ສາມາດຕິດຕັ້ງໃສ່ໂທລະພາບຂອງທ່ານໄດ້."
+ "ບໍ່ສາມາດຕິດຕັ້ງ %1$s ໃນໂທລະສັບຂອງທ່ານໄດ້."
+ "ເປີດ"
+ "ຜູ້ເບິ່ງແຍງລະບົບຂອງທ່ານບໍ່ອະນຸຍາດໃຫ້ຕິດຕັ້ງແອັບທີ່ໄດ້ມາຈາກແຫຼ່ງທີ່ບໍ່ຮູ້ຈັກ"
+ "ຜູ້ໃຊ້ນີ້ບໍ່ສາມາດຕິດຕັ້ງແອັບທີ່ບໍ່ຮູ້ຈັກໄດ້"
+ "ຜູ້ໃຊ້ນີ້ບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຕິດຕັ້ງແອັບໄດ້"
+ "ຕົກລົງ"
+ "ຈັດການແອັບຯ"
+ "ພື້ນທີ່ຫວ່າງບໍ່ພຽງພໍ"
+ "ບໍ່ສາມາດຕິດຕັ້ງ %1$s ໄດ້. ກະລຸນາລຶບຂໍ້ມູນທີ່ບໍ່ຈຳເປັນອອກ ເພື່ອໃຫ້ມີບ່ອນຈັດເກັບຂໍ້ມູນຫວ່າງເພີ່ມຂຶ້ນ ແລ້ວລອງໃໝ່ອີກຄັ້ງ."
+ "ບໍ່ພົບເຫັນແອັບຯ"
+ "ບໍ່ພົບແອັບຯໃນລາຍການຂອງແອັບຯທີ່ຕິດຕັ້ງແລ້ວ."
+ "ບໍ່ອະນຸຍາດແລ້ວ"
+ "ຜູ້ໃຊ້ປັດຈຸບັນບໍ່ໄດ້ຮັບອະນຸຍາດໃຫ້ຖອນການຕິດຕັ້ງນີ້ໄດ້."
+ "ຜິດພາດ"
+ "ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບໄດ້."
+ "ຖອນການຕິດຕັ້ງແອັບຯ"
+ "ຖອນການຕິດຕັ້ງອັບເດດ"
+ "%1$s ແມ່ນສ່ວນນຶ່ງຂອງແອັບຯຂ້າງລຸ່ມ:"
+ "ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບຯນີ້ບໍ່?"
+ "ທ່ານຕ້ອງການທີ່ຈະຖອນການຕິດຕັ້ງແອັບຯນີ້ ສຳລັງຜູ່ໃຊ້""ທຸກຄົນ""ບໍ່? ແອັບພລິເຄຊັນ ແລະຂໍ້ມູນຂອງມັນຈະຖືກລຶບອອກ ຈາກຜູ່ໃຊ້""ທັງໝົດ""ໃນອຸປະກອນນີ້."
+ "ທ່ານຕ້ອງການຖອນການຕິດຕັ້ງແອັບຯນີ້ສຳລັບຜູ່ໃຊ້ %1$s ບໍ່?"
+ "ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ."
+ "ແທນທີ່ແອັບນີ້ດ້ວຍເວີຊັນທີ່ມາຈາກໂຮງງານບໍ? ຂໍ້ມູນທັງໝົດຈະຖືກລຶບອອກ ເຊິ່ງມີຜົນກັບຜູ້ໃຊ້ອຸປະກອນນີ້ທຸກຄົນ ຮວມທັງຄົນທີ່ມີໂປຣໄຟລ໌ບ່ອນເຮັດວຽກນຳ."
+ "ກຳລັງຖອນການຕິດຕັ້ງ"
+ "ຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ"
+ "ກຳລັງຖອນການຕິດຕັ້ງ..."
+ "ກຳລັງຖອນການຕິດຕັ້ງ %1$s…"
+ "ຖອນການຕິດຕັ້ງສຳເລັດແລ້ວ."
+ "ຖອນການຕິດຕັ້ງ %1$s ແລ້ວ"
+ "ການຖອນການຕິດຕັ້ງບໍ່ສຳເລັດ."
+ "ຖອນການຕິດຕັ້ງ %1$s ບໍ່ສຳເລັດ."
+ "ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບອຸປະກອນທີ່ເຮັດວຽກຢູ່ໄດ້"
+ "ບໍ່ສາມາດຖອນການຕິດຕັ້ງແອັບຜູ້ເບິ່ງແຍງລະບົບທີ່ເຮັດວຽກຢູ່ສຳລັບ %1$s ໄດ້"
+ "ແອັບນີ້ຈຳເປັນສຳລັບບາງຜູ້ໃຊ້ ຫຼື ບາງໂປຣໄຟລ໌ ແລະ ຖືກຖອນການຕິດຕັ້ງສຳລັບຄົນອື່ນແລ້ວ"
+ "ແອັບນີ້ຈຳເປັນຕ້ອງໃຊ້ກັບໂປຣໄຟລ໌ຂອງທ່ານ ແລະ ບໍ່ສາມາດຖອນການຕິດຕັ້ງໄດ້."
+ "ແອັບຯນີ້ຕ້ອງໃຊ້ໂດຍຜູ່ເບິ່ງແຍງລະບົບຂອງທ່ານ ແລະບໍ່ສາມາດຖອນການຕິດຕັ້ງໄດ້."
+ "ຈັດການແອັບຜູ້ເບິ່ງແຍງອຸປະກອນ"
+ "ຈັດການຜູ້ໃຊ້"
+ "%1$s ບໍ່ສາມາດຖອນອອກໄດ້."
+ "ເກີດບັນຫາໃນການວິເຄາະແພັກເກດ."
+ "ໃໝ່"
+ "ທັງໝົດ"
+ "ຄວາມເປັນສ່ວນຕົວ"
+ "ການເຂົ້າເຖິງອຸປະກອນ"
+ "ອັບເດດນີ້ບໍ່ຕ້ອງການການອະນຸຍາດໃໝ່."
+ "ປະຕິເສດ"
+ "ຂໍ້ມູນເພີ່ມເຕີມ"
+ "ຢືນຢັນປະຕິເສດ"
+ "%1$s ໃນ %2$s"
+ "ອະນຸຍາດໃຫ້ <b>%1$s</b> ສາມາດ %2$s ບໍ?"
+ "ອະນຸຍາດ <b>%1$s</b> ໃຫ້ໃຊ້ %2$s ໄດ້ທຸກເທື່ອບໍ?"
+ "ໃນເວລາໃຊ້ແອັບເທົ່ານັ້ນ"
+ "ທຸກເທື່ອ"
+ "ປະຕິເສດ ແລະ ຢ່າຖາມອີກ"
+ "ປິດໄວ້ %1$d ສິດອະນຸຍາດແລ້ວ"
+ "ປິດໄວ້ທັງໝົດແລ້ວ"
+ "ບໍ່ມີອັນໃດປິດການນຳໃຊ້ໄວ້"
+ "ອະນຸຍາດ"
+ "ແອັບ"
+ "ການອະນຸຍາດແອັບ"
+ "ບໍ່ຕ້ອງຖາມອີກ"
+ "ບໍ່ມີການອະນຸຍາດ"
+ "ການອະນຸຍາດເພີ່ມເຕີມ"
+ "ເປີດຂໍ້ມູນແອັບ"
+
+ %1$d ເພີ່ມເຕີມ
+ %1$d ເພີ່ມເຕີມ
+
+ "ແອັບນີ້ຖືກອອກແບບມາສຳລັບ Android ເວີຊັນເກົ່າ. ການປະຕິເສດການອະນຸຍາດອາດຈະເຮັດໃຫ້ມັນບໍ່ເຮັດວຽກຕາມຕ້ອງການໄດ້ອີກ."
+ "ເຮັດການດຳເນີນການທີ່ບໍ່ຮູ້ຈັກ"
+ "%1$d ໃນ %2$d ແອັບໄດ້ຮັບອະນຸຍາດແລ້ວ"
+ "ສະແດງລະບົບ"
+ "ເຊື່ອງລະບົບ"
+ "ບໍ່ມີແອັບ"
+ "ການຕັ້ງຄ່າທີ່ຕັ້ງ"
+ "%1$s ແມ່ນຜູ້ໃຫ້ບໍລິການເລື່ອງການບໍລິການທີ່ຕັ້ງສຳລັບອຸປະກອນນີ້. ການເຂົ້າເຖິງທີ່ຕັ້ງແມ່ນສາມາດດັດແປງໄດ້ຈາກການຕັ້ງຄ່າທີ່ຕັ້ງ."
+ "ຖ້າທ່ານປະຕິເສດການອະນຸຍາດນີ້, ຄຸນສົມບັດໃຊ້ງານພື້ນຖານຂອງອຸປະກອນຂອງທ່ານອາດຈະບໍ່ເຮັດໜ້າທີ່ຕາມທີ່ກຳນົດໄວ້."
+ "ບັງຄັບໃຊ້ຕາມນະໂຍບາຍແລ້ວ"
+ "ປິດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນຫຼັງຕາມນະໂຍບາຍແລ້ວ"
+ "ເປີດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນຫຼັງຕາມນະໂຍບາຍແລ້ວ"
+ "ເປີດນຳໃຊ້ການເຂົ້າເຖິງໃນພື້ນໜ້າຕາມນະໂຍບາຍແລ້ວ"
+ "ຄວບຄຸມໂດຍຜູ້ເບິ່ງແຍງລະບົບ"
+
+
+
+ "ທຸກເທື່ອ"
+ "ໃນເວລາໃຊ້ແອັບເທົ່ານັ້ນ"
+ "ບໍ່ເລີຍ"
+ "ກຳລັງໂຫລດ..."
+ "ທຸກການອະນຸຍາດ"
+ "ຄວາມສາມາດອື່ນຂອງແອັບ"
+ "ການຮ້ອງຂໍການອະນຸຍາດ"
+ "ກວດພົບການວາງຊ້ອນໜ້າຈໍແລ້ວ"
+ "ເພື່ອປ່ຽນແປງການຕັ້ງຄ່າການອະນຸຍາດນີ້, ກ່ອນອື່ນໝົດທ່ານຕ້ອງປິດການວາງຊ້ອນໜ້າຈໍຈາກແອັບການຕັ້ງຄ່າ"
+ "ເປີດການຕັ້ງຄ່າ"
+ "Android Wear"
+ "ຕິດຕັ້ງ/ຖອນຕິດຕັ້ງ ຄຳສັ່ງທີ່ບໍ່ຮອງຮັບຢູ່ໃນ Wear."
+ "ເລືອກວ່າຈະອະນຸຍາດໃຫ້ <b>%1$s</b> ເຂົ້າເຖິງຫຍັງໄດ້ແດ່"
+ "ອັບເດດ <b>%1$s</b> ແລ້ວ. ກະລຸນາເລືອກວ່າຈະໃຫ້ແອັບນີ້ເຂົ້າເຖິງຫຍັງໄດ້ແດ່."
+ "ຍົກເລີກ"
+ "ສືບຕໍ່"
+ "ການອະນຸຍາດໃໝ່"
+ "ການອະນຸຍາດປັດຈຸບັນ"
+ "ກຳລັງຮຽງແອັບ…"
+ "ບໍ່ຮູ້ຈັກ"
+ "ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ແທັບເລັດຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."
+ "ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ໂທລະທັດຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."
+ "ເພື່ອຄວາມປອດໄພຂອງທ່ານ, ໂທລະສັບຂອງທ່ານບໍ່ສາມາດຕິດຕັ້ງແອັບຈາກແຫລ່ງຂໍ້ມູນນີ້ໄດ້."
+ "ໂທລະສັບ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."
+ "ແທັບເລັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."
+ "ໂທລະທັດ ແລະ ຂໍ້ມູນສ່ວນຕົວຂອງທ່ານອາດຖືກໂຈມຕີໄດ້ໂດຍແອັບທີ່ບໍ່ຮູ້ຈັກ. ໂດຍການຕິດຕັ້ງແອັບນີ້, ແມ່ນທ່ານຍອມຮັບວ່າທ່ານຈະຮັບຜິດຊອບຕໍ່ຄວາມເສຍຫາຍໃດໆກໍຕາມທີ່ເກີດຂຶ້ນຕໍ່ໂທລະທັດຂອງທ່ານ ຫຼື ການສູນເສຍຂໍ້ມູນທີ່ອາດເກີດຈາກການນຳໃຊ້ມັນ."
+ "ດຳເນີນການຕໍ່"
+ "ການຕັ້ງຄ່າ"
+ "ກຳລັງຕິດຕັ້ງ/ຖອດຖອນແອັບ Wear"
+
diff --git a/packages/PackageInstaller/res/values-lt-television/strings.xml b/packages/PackageInstaller/res/values-lt-television/strings.xml
new file mode 100644
index 0000000000000..dee6de74133fd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Atmesti ir daugiau neklausti"
+ "Tai vėliau galėsite pakeisti skiltyje „Nustatymai > Programos“"
+ "%1$s / %2$s"
+ "Rodyti sistemos programas"
+ "Programos leidimai"
+ "Programos leidimai"
+ "%1$s leidimai"
+ "Papildomi leidimai"
+ "%1$s leidimai"
+
diff --git a/packages/PackageInstaller/res/values-lt-watch/strings.xml b/packages/PackageInstaller/res/values-lt-watch/strings.xml
new file mode 100644
index 0000000000000..85eebcf4a64e5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Atmesti, daugiau neklausti"
+ "%1$s / %2$s"
+ "Rodyti sistemos programas"
+ "Negalima pakeisti"
+ "Taip"
+ "Atšaukti"
+
diff --git a/packages/PackageInstaller/res/values-lt/strings.xml b/packages/PackageInstaller/res/values-lt/strings.xml
new file mode 100644
index 0000000000000..79546a9dccf12
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lt/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Paketo įdiegimo programa"
+ "Kitas"
+ "Įdiegti"
+ "Atlikta"
+ "Atšaukti"
+ "Diegiama..."
+ "Įdiegiama „%1$s“…"
+ "Programa įdiegta."
+ "Ar norite įdiegti šią programą? Jai bus suteikta prieiga prie:"
+ "Ar norite įdiegti šią programą? Jai nereikalinga jokia speciali prieiga."
+ "Ar norite įdiegti šios esamos programos naujinį? Neprarasite esamų duomenų. Atnaujinus programą bus suteikta prieiga prie:"
+ "Ar norite įdiegti šios integruotos programos naujinį? Neprarasite esamų duomenų. Atnaujinus programą bus suteikta prieiga prie:"
+ "Ar norite įdiegti šios esamos programos naujinį? Neprarasite esamų duomenų. Nereikia jokios specialios prieigos."
+ "Ar norite įdiegti šios integruotos programos naujinį? Neprarasite esamų duomenų. Nereikia jokios specialios prieigos."
+ "Programa neįdiegta."
+ "Paketas užblokuotas ir negali būti įdiegtas."
+ "Programa neįdiegta, nes paketas nesuderinamas su esamu paketu."
+ "Programa neįdiegta, nes ji nesuderinama su planšetiniu kompiuteriu."
+ "Ši programa nesuderinama su jūsų TV."
+ "Programa neįdiegta, nes ji nesuderinama su telefonu."
+ "Programa neįdiegta, nes panašu, kad paketas netinkamas."
+ "Jūsų planšetiniame kompiuteryje nepavyko įdiegti „%1$s“."
+ "Nepavyko programos „%1$s“ įdiegti jūsų TV."
+ "Jūsų telefone nepavyko įdiegti „%1$s“."
+ "Atidaryti"
+ "Jūsų administratorius neleidžia diegti programų, gautų iš nežinomų šaltinių"
+ "Šis naudotojas negali diegti nežinomų programų"
+ "Šiam naudotojui neleidžiama diegti programų"
+ "Gerai"
+ "Tvarkyti programas"
+ "Nėra vietos"
+ "Nepavyko įdiegti „%1$s“. Atlaisvinkite vietos ir bandykite dar kartą."
+ "Programa nerasta"
+ "Programa nerasta įdiegtų programų sąraše."
+ "Neleidžiama"
+ "Dabartiniam naudotojui neleidžiama atlikti šio pašalinimo veiksmo."
+ "Klaida"
+ "Nepavyko įdiegti programos."
+ "Pašalinti programą"
+ "Pašalinti naujinį"
+ "„%1$s“ yra šios programos dalis:"
+ "Ar norite pašalinti šią programą?"
+ "Ar norite pašalinti šią programą ""visiems"" naudotojams? Programa ir jos duomenys bus pašalinti iš ""visų"" naudotojų įrenginyje."
+ "Ar norite pašalinti šią naudotojo %1$s programą?"
+ "Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti."
+ "Pakeisti šios programos versiją į gamyklinę? Visi duomenys bus pašalinti. Tai paveiks visus šio įrenginio naudotojus, įskaitant turinčius darbo profilius."
+ "Vykdomi įdiegimai"
+ "Nepavykę įdiegimai"
+ "Pašalinama..."
+ "Pašalinama „%1$s“…"
+ "Pašalinimas baigtas."
+ "Paketas „%1$s“ pašalintas"
+ "Nepavyko pašalinti."
+ "„%1$s“ pašalinimo veiksmas nesėkmingas."
+ "Negalima pašalinti aktyvios įrenginio administravimo programos"
+ "Negalima pašalinti aktyvios naudotojo %1$s įrenginio administravimo programos"
+ "Ši programa reikalinga kai kuriems naudotojams ar kai kuriuose profiliuose ir buvo pašalinta kitur"
+ "Ši programa reikalinga jūsų profilyje ir jos negalima pašalinti."
+ "Ši programa reikalinga jūsų įrenginio administratoriui ir jos negalima pašalinti."
+ "Tvarkyti įrenginio administravimo programas"
+ "Valdyti naudotojus"
+ "Nepavyko pašalinti „%1$s“."
+ "Analizuojant paketą iškilo problema."
+ "Naujiena"
+ "Visi"
+ "Privatumas"
+ "Prieiga prie įreng."
+ "Šiam naujiniui nereikalingi nauji leidimai."
+ "Atmesti"
+ "Daugiau informacijos"
+ "Vis tiek atmesti"
+ "%1$s iš %2$s"
+ "Leisti programai <b>%1$s</b> %2$s?"
+ "Visada leisti programai <b>%1$s</b> %2$s?"
+ "Tik naudojant programą"
+ "Visada"
+ "Atmesti ir daugiau neklausti"
+ "Išjungta leidimų: %1$d"
+ "visi leidimai išjungti"
+ "nė vienas leidimas neišjungtas"
+ "Leisti"
+ "Programos"
+ "Programos leidimai"
+ "Daugiau neklausti"
+ "Nėra jokių leidimų"
+ "Papildomi leidimai"
+ "Atidaryti programos informaciją"
+
+ Dar %1$d
+ Dar %1$d
+ Dar %1$d
+ Dar %1$d
+
+ "Ši programa skirta senesnės versijos „Android“. Uždraudus leidimą ji gali nebeveikti kaip numatyta."
+ "atlieka nežinomą veiksmą"
+ "Leidžiama programų: %1$d iš %2$d"
+ "Rodyti sistemą"
+ "Slėpti sistemą"
+ "Nėra jokių programų"
+ "Vietovės nustatymai"
+ "„%1$s“ yra šio įrenginio vietovės paslaugų teikėjas. Vietovės pasiekiamumą galima keisti vietovės nustatymuose."
+ "Jei uždrausite šį leidimą, pagrindinės įrenginio funkcijos gali nebeveikti, kaip numatyta."
+ "Reikalaujama pagal politikos nuostatas"
+ "Prieiga fone išjungta pagal politiką"
+ "Prieiga fone įgalinta pagal politiką"
+ "Prieiga priekiniame plane įgalinta pagal politiką"
+ "Valdo administratorius"
+
+
+
+ "Visada"
+ "Tik naudojant programą"
+ "Niekada"
+ "Įkeliama..."
+ "Visi leidimai"
+ "Kitos programos galimybės"
+ "Leidimo užklausa"
+ "Aptikta ekrano perdanga"
+ "Jei norite pakeisti šį leidimo nustatymą, pirmiausia turite išjungti ekrano perdangą skiltyje „Nustatymai > Programos“"
+ "Atidaryti nustatymus"
+ "Android Wear"
+ "Diegimo / pašalinimo veiksmai nepalaikomi sistemoje „Wear“."
+ "Pasirinkite, ką norite leisti programai <b>%1$s</b> pasiekti"
+ "Programa <b>%1$s</b> buvo atnaujinta. Pasirinkite, ką norite leisti šiai programai pasiekti."
+ "Atšaukti"
+ "Tęsti"
+ "Nauji leidimai"
+ "Dabartiniai leidimai"
+ "Programa pateikiama etapais…"
+ "Nežinoma"
+ "Saugos sumetimais planšetiniame kompiuteryje neleidžiama diegti nežinomų programų iš šio šaltinio."
+ "Saugos sumetimais TV neleidžiama diegti nežinomų programų iš šio šaltinio."
+ "Saugos sumetimais telefone neleidžiama diegti nežinomų programų iš šio šaltinio."
+ "Telefonas ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą telefonui arba prarastus duomenis dėl šios programos naudojimo."
+ "Planšetinis kompiuteris ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą planšetiniam kompiuteriui arba prarastus duomenis dėl šios programos naudojimo."
+ "TV ir asmeniniai duomenys labiau pažeidžiami įdiegus nežinomų programų. Įdiegdami šią programą sutinkate, kad patys esate atsakingi už žalą TV arba prarastus duomenis dėl šios programos naudojimo."
+ "Tęsti"
+ "Nustatymai"
+ "Įdiegiamos / pašalinamos „Wear“ program."
+
diff --git a/packages/PackageInstaller/res/values-lv-television/strings.xml b/packages/PackageInstaller/res/values-lv-television/strings.xml
new file mode 100644
index 0000000000000..f01dfd282b306
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Noraidīt un vairs nejautāt"
+ "Vēlāk varat veikt izmaiņas sadaļā Iestatījumi > Lietotnes."
+ "%1$s. no %2$s"
+ "Rādīt sistēmas lietotnes"
+ "Lietotņu atļaujas"
+ "Lietotņu atļaujas"
+ "Lietotnes %1$s atļaujas"
+ "Papildu atļaujas"
+ "Lietotnes %1$s atļaujas"
+
diff --git a/packages/PackageInstaller/res/values-lv-watch/strings.xml b/packages/PackageInstaller/res/values-lv-watch/strings.xml
new file mode 100644
index 0000000000000..29aef48d0445a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Neatļaut un vairs nejautāt"
+ "%1$s. no %2$s"
+ "Rādīt sistēmas lietotnes"
+ "Nevar mainīt"
+ "Jā"
+ "Atcelt"
+
diff --git a/packages/PackageInstaller/res/values-lv/strings.xml b/packages/PackageInstaller/res/values-lv/strings.xml
new file mode 100644
index 0000000000000..40ddf7474ada5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-lv/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Pakotnes instalēšanas programma"
+ "Tālāk"
+ "Instalēt"
+ "Gatavs"
+ "Atcelt"
+ "Notiek instalēšana..."
+ "Notiek pakotnes %1$s instalēšana…"
+ "Lietotne ir instalēta."
+ "Vai vēlaties instalēt šo lietojumprogrammu? Tā iegūs piekļuvi:"
+ "Vai vēlaties instalēt šo lietojumprogrammu? Tai nav nepieciešamas īpašas piekļuves atļaujas."
+ "Vai vēlaties instalēt šīs lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Atjauninātajai lietojumprogrammai būs piekļuve:"
+ "Vai vēlaties instalēt šīs iebūvētās lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Atjauninātajai lietojumprogrammai būs piekļuve:"
+ "Vai vēlaties instalēt šīs lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Tam nav nepieciešama īpaša piekļuves atļauja."
+ "Vai vēlaties instalēt šīs iebūvētās lietojumprogrammas atjauninājumu? Esošie dati netiks zaudēti. Tam nav nepieciešama īpaša piekļuves atļauja."
+ "Lietotne nav instalēta."
+ "Pakotnes instalēšana tika bloķēta."
+ "Lietotne netika instalēta, jo rodas pakotnes konflikts ar esošo pakotni."
+ "Lietotne netika instalēta, jo tā nav saderīga ar jūsu planšetdatoru."
+ "Šī lietotne nav saderīga ar jūsu televizoru."
+ "Lietotne netika instalēta, jo tā nav saderīga ar jūsu tālruni."
+ "Lietotne netika instalēta, jo šķiet, ka pakotne nav derīga."
+ "Lietotni %1$s nevarēja instalēt planšetdatorā."
+ "Lietotni %1$s nevarēja instalēt jūsu televizorā."
+ "Lietotni %1$s nevarēja instalēt tālrunī."
+ "Atvērt"
+ "Jūsu administrators neļauj instalēt lietotnes, kas iegūtas no nezināmiem avotiem."
+ "Šis lietotājs nevar instalēt lietotnes, kas iegūtas no nezināmiem avotiem."
+ "Šim lietotājam nav atļauts instalēt lietotnes"
+ "Labi"
+ "Pārvaldīt lietotnes"
+ "Nav brīvas vietas"
+ "Lietotni %1$s nevarēja instalēt. Atbrīvojiet vietu un mēģiniet vēlreiz."
+ "Lietotne nav atrasta"
+ "Šī lietotne netika atrasta instalēto lietotņu sarakstā."
+ "Nav atļauts"
+ "Pašreizējam lietotājam nav atļauts veikt atinstalēšanu."
+ "Kļūda"
+ "Nevarēja atinstalēt lietotni."
+ "Atinstalēt lietotni"
+ "Atinstalēt atjauninājumu"
+ "%1$s ir daļa no šādas lietotnes:"
+ "Vai vēlaties atinstalēt šo lietotni?"
+ "Vai vēlaties atinstalēt šo lietotni ""visiem"" lietotājiem? Šī lietojumprogramma un tās dati tiks noņemti no ""visiem"" ierīces lietotāju kontiem."
+ "Vai vēlaties atinstalēt šo lietotni lietotājam %1$s?"
+ "Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti."
+ "Vai aizstāt šo lietotni ar rūpnīcas versiju? Visi dati tiks noņemti. Tas ietekmēs visu šīs ierīces lietotāju (arī to lietotāju, kuriem ir darba profili) datus."
+ "Pašlaik veiktie atinstalēšanas gadījumi"
+ "Neizdevušies atinstalēšanas gadījumi"
+ "Notiek atinstalēšana..."
+ "Notiek lietotnes %1$s atinstalēšana…"
+ "Atinstalēšana ir pabeigta."
+ "Lietotne %1$s ir atinstalēta"
+ "Atinstalēšana neizdevās."
+ "Lietotnes %1$s atinstalēšana nebija sekmīga."
+ "Neizdevās atinstalēt aktīvas ierīces administratora lietotnes."
+ "Neizdevās atinstalēt aktīvas ierīces administratora lietotni %1$s."
+ "Šī lietotne ir nepieciešama dažiem lietotājiem vai profiliem un tika atinstalēta citiem"
+ "Šī lietotne ir nepieciešama jūsu profilam, tāpēc to nevar atinstalēt."
+ "Ierīces administrators ir noteicis, ka lietotne ir obligāta un to nevar atinstalēt."
+ "Pārvaldīt ierīces administratora lietotnes"
+ "Pārvaldīt lietotājus"
+ "Lietotni %1$s nevarēja atinstalēt."
+ "Parsējot pakotni, radās problēma."
+ "Jauna!"
+ "Visas"
+ "Konfidencialitāte"
+ "Piekļuve ierīcei"
+ "Šim atjauninājumam nav nepieciešamas jaunas atļaujas."
+ "Neatļaut"
+ "Plašāka informācija"
+ "Tomēr noraidīt"
+ "%1$s. no %2$s"
+ "Vai atļaut lietotnei <b>%1$s</b> %2$s?"
+ "Vai vienmēr atļaut lietotnei <b>%1$s</b> %2$s?"
+ "Tikai izmantojot lietotni"
+ "Vienmēr"
+ "Noraidīt un vairs nejautāt"
+ "%1$d atspējotas"
+ "visas atspējotas"
+ "neviena nav atspējota"
+ "Atļaut"
+ "Lietotnes"
+ "Lietotnes atļaujas"
+ "Turpmāk vairs nejautāt"
+ "Nav atļauju"
+ "Papildu atļaujas"
+ "Atvērt lietotnes informāciju"
+
+ Vēl %1$d
+ Vēl %1$d
+ Vēl %1$d
+
+ "Šī lietotne ir paredzēta vecākai Android versijai. Ja noraidīsiet atļauju, iespējams, netiks nodrošināta paredzētā lietotnes darbība."
+ "veikt nezināmu darbību"
+ "Atļautas %1$d lietotnes no %2$d"
+ "Rādīt sistēmas lietotnes"
+ "Slēpt sistēmas lietotnes"
+ "Nav lietotņu"
+ "Atrašanās vietas iestatījumi"
+ "%1$s nodrošina šai ierīcei atrašanās vietu pakalpojumus. Piekļuves iestatījumus atrašanās vietas datiem var mainīt atrašanās vietas iestatījumos."
+ "Ja nepiešķirsiet šo atļauju, ierīces pamatfunkcijas, iespējams, vairs nedarbosies, kā paredzēts."
+ "Īstenota saskaņā ar politiku"
+ "Piekļuve fonā atspējota saskaņā ar politiku"
+ "Piekļuve fonā iespējota saskaņā ar politiku"
+ "Piekļuve priekšplānā iespējota saskaņā ar politiku"
+ "Kontrolē administrators"
+
+
+
+ "Vienmēr"
+ "Tikai izmantojot lietotni"
+ "Nekad"
+ "Notiek ielāde..."
+ "Visas atļaujas"
+ "Citas lietotnes atļaujas"
+ "Atļaujas pieprasījums"
+ "Konstatēts ekrāna pārklājums"
+ "Lai mainītu šo atļaujas iestatījumu, vispirms sadaļā “Iestatījumi un lietotnes” izslēdziet ekrāna pārklājumu."
+ "Atvērt iestatījumus"
+ "Android Wear"
+ "Wear ierīcē netiek atbalstīta instalēšana/atinstalēšana"
+ "Izvēlieties, kādas piekļuves atļaujas piešķirt lietotnei <b>%1$s</b>"
+ "Lietotne <b>%1$s</b> ir atjaunināta. Izvēlieties, kādas piekļuves atļaujas tai piešķirt."
+ "Atcelt"
+ "Turpināt"
+ "Jaunas atļaujas"
+ "Pašreizējās atļaujas"
+ "Lietotne tiek izstādīta…"
+ "Nezināms"
+ "Drošības nolūkos jūsu planšetdatorā nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."
+ "Drošības nolūkos jūsu televizorā nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."
+ "Drošības nolūkos jūsu tālrunī nedrīkst instalēt no šī avota iegūtas nezināmas lietotnes."
+ "Jūsu tālruņa un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par tālruņa bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."
+ "Jūsu planšetdatora un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par planšetdatora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."
+ "Jūsu televizora un personas dati ir neaizsargātāki pret uzbrukumiem no nezināmām lietotnēm. Instalējot šo lietotni, jūs piekrītat, ka esat atbildīgs par televizora bojājumiem vai datu zudumu, kas var rasties lietotnes dēļ."
+ "Turpināt"
+ "Iestatījumi"
+ "Wear lietotņu instalēšana/atinstalēšana"
+
diff --git a/packages/PackageInstaller/res/values-mk-television/strings.xml b/packages/PackageInstaller/res/values-mk-television/strings.xml
new file mode 100644
index 0000000000000..bb3ea9213a205
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Одбиј и не прашувај повторно"
+ "Може да го промените ова подоцна во Поставки > Апликации"
+ "%1$s/%2$s"
+ "Прикажи ги системските апликации"
+ "Дозволи за апликацијата"
+ "Дозволи за апликацијата"
+ "Дозволи за %1$s"
+ "Дополнителни дозволи"
+ "Дозволи за %1$s"
+
diff --git a/packages/PackageInstaller/res/values-mk-watch/strings.xml b/packages/PackageInstaller/res/values-mk-watch/strings.xml
new file mode 100644
index 0000000000000..5906f56d5bbfb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Одбиј, не прашувај повторно"
+ "%1$s/%2$s"
+ "Прикажи ги системските апликации"
+ "Не може да се смени"
+ "Да"
+ "Откажи"
+
diff --git a/packages/PackageInstaller/res/values-mk/strings.xml b/packages/PackageInstaller/res/values-mk/strings.xml
new file mode 100644
index 0000000000000..8cf208e4e8178
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mk/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Инсталатор на пакет"
+ "Следно"
+ "Инсталирај"
+ "Готово"
+ "Откажи"
+ "Се инсталира..."
+ "Се инсталира %1$s…"
+ "Апликацијата е инсталирана."
+ "Дали сакате да ја инсталирате оваа апликација? Ќе добие пристап до:"
+ "Дали сакате да ја инсталирате оваа апликација? Не бара никаков посебен пристап."
+ "Дали сакате да инсталирате надградба на оваа постоечка апликација? Вашите постоечки податоци нема да се изгубат. Ажурираната апликација ќе добие пристап до:"
+ "Дали сакате да инсталирате надградба на оваа вградена апликација? Вашите постоечки податоци нема да се изгубат. Ажурираната апликација ќе добие пристап до:"
+ "Дали сакате да инсталирате надградба на оваа постоечка апликација? Вашите постоечки податоци нема да се изгубат. Таа не бара никаков посебен пристап."
+ "Дали сакате да инсталирате надградба на оваа вградена апликација? Вашите постоечки податоци нема да се изгубат. Таа не бара никаков посебен пристап."
+ "Апликацијата не е инсталирана."
+ "Инсталирањето на пакетот е блокирано."
+ "Апликација што не е инсталирана како пакет е во конфликт со постоен пакет."
+ "Апликација што не е инсталирана како апликација не е компатибилна со вашиот таблет."
+ "Оваа апликација не е компатибилна со вашиот телевизор."
+ "Апликација што не е инсталирана како апликација не е компатибилна со вашиот телефон."
+ "Апликација што не е инсталирана како пакет се чини дека е неважечка."
+ "%1$s не можеше да се инсталира на вашиот таблет."
+ "%1$s не може да се инсталира на вашиот телевизор."
+ "%1$s не можеше да се инсталира на вашиот телефон."
+ "Отвори"
+ "Вашиот администратор не дозволува инсталација на апликации добиени од непознати извори"
+ "Корисников не може да инсталира непознати апликации"
+ "На корисников не му е дозволено да инсталира апликации"
+ "Во ред"
+ "Управувај со апликации"
+ "Нема простор"
+ "%1$s не можеше да се инсталира. Ослободете простор и обидете се повторно."
+ "Апликацијата не е пронајдена"
+ "Апликацијата не е пронајдена во списокот инсталирани апликации."
+ "Не е дозволено"
+ "Тековниот корисник нема дозвола да ја изведе деинсталацијава."
+ "Грешка"
+ "Не можеше да се деинсталира апликацијата."
+ "Деинсталирај апликација"
+ "Деинсталирај ажурирање"
+ "%1$s е дел од следната апликација:"
+ "Дали сакате да ја деинсталирате оваа апликација?"
+ "Дали сакате да ја деинсталирате оваа апликација за ""сите"" корисници? Апликацијата и нејзините податоци ќе се отстранат од ""сите"" корисници на уредот."
+ "Дали сакате да ја деинсталирате апликацијава за корисникот %1$s?"
+ "Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат."
+ "Сакате да ја замените оваа апликација со фабричката верзија? Сите податоци ќе се отстранат. Тоа важи за сите корисници на овој уред, вклучувајќи ги и тие со работни профили."
+ "Деинсталации во тек"
+ "Неуспешни деинсталации"
+ "Се деинсталира..."
+ "Се деинсталира %1$s…"
+ "Деинсталирањето заврши."
+ "%1$s е деинсталиран"
+ "Деинсталирањето е неуспешно."
+ "Деинсталирањето на %1$s е неуспешно."
+ "Не може да се деинсталира активна апликација на администраторот на уредот"
+ "Не може да се деинсталира активна апликација на администраторот на уредот за %1$s"
+ "Апликацијата е неопходна за некои корисници или профили, а за другите е деинсталирана"
+ "Апликацијата е потребна за вашиот профил и не може да се деинсталира."
+ "Апликацијата ја бара администраторот на вашиот уред и не може да се деинсталира."
+ "Управувај со аплик. за администраторот на уредот"
+ "Управувај со корисници"
+ "%1$s не може да се деинсталира."
+ "Настана проблем при разложување на пакетот."
+ "Ново"
+ "Сè"
+ "Приватност"
+ "Пристап кон уредот"
+ "Ова ажурирање не бара нови дозволи."
+ "Одбиј"
+ "Повеќе информации"
+ "Сепак одбиј"
+ "%1$s од %2$s"
+ "Дозволете <b>%1$s</b> да %2$s?"
+ "Дали секогаш да се дозволи <b>%1$s</b> да %2$s?"
+ "Само додека се користи апликацијата"
+ "Секогаш"
+ "Одбиј и не прашувај повторно"
+ "Оневозможени се %1$d"
+ "сите се оневозможени"
+ "ниедна не е оневозможена"
+ "Овозможи"
+ "Апликации"
+ "Дозволи за апликацијата"
+ "Не прашувај повторно"
+ "Нема дозволи"
+ "Дополнителни дозволи"
+ "Отвора информации за апликација"
+
+ Уште %1$d
+ Уште %1$d
+
+ "Оваа апликација е дизајнирана за постара верзија на Android. Одбивањето на дозволата може да предизвика веќе да не функционира како што треба."
+ "изврши непознато дејство"
+ "Дозволени се %1$d од %2$d апликации"
+ "Прикажи систем"
+ "Сокриј систем"
+ "Нема апликации"
+ "Поставки за локација"
+ "%1$s е давател на услуги според локација за овој уред. Пристапот до локацијата може да се смени од Поставките за локација."
+ "Ако ја одбиете оваа дозвола, основните функции на вашиот уред можеби веќе нема да функционираат како што треба."
+ "Наложено од политиката"
+ "Пристапот од заднина е оневозможен со правилото"
+ "Пристапот од заднина е овозможен со правилото"
+ "Пристапот од преден план е овозможен со правилото"
+ "Контролирано од администраторот"
+
+
+
+ "Секогаш"
+ "Само додека се користи аплик."
+ "Никогаш"
+ "Се вчитува..."
+ "Сите дозволи"
+ "Други можности на апликацијата"
+ "Барање дозвола"
+ "Откривме преклопување на екранот"
+ "За да ја измените оваа поставка за дозвола, прво мора да го исклучите преклопувањето на екранот од Поставки > Апликации"
+ "Отвори поставки"
+ "Android Wear"
+ "Дејствата инсталирај/деинсталирај не се поддржани на Wear."
+ "Изберете што да ѝ се овозможи на <b>%1$s</b> за пристап"
+ "<b>%1$s</b> е ажурирана. Изберете што да ѝ се овозможи на оваа апликација за пристап."
+ "Откажи"
+ "Продолжи"
+ "Нови дозволи"
+ "Тековни дозволи"
+ "Апликацијата се поставува…"
+ "Непознато"
+ "За ваша безбедност, таблетот нема дозвола за инсталирање непознати апликации од изворов."
+ "За ваша безбедност, телевизорот нема дозвола за инсталирање непознати апликации од изворов."
+ "За ваша безбедност, телефонот нема дозвола за инсталирање непознати апликации од изворов."
+ "Телефонот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на телефонот или губењето податоци што може да произлезат од нејзиното користење."
+ "Таблетот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на таблетот или губењето податоци што може да произлезат од нејзиното користење."
+ "Телевизорот и личните податоци се поподложни на напади од непознати апликации. Ако ја инсталирате апликацијава, се согласувате дека сте одговорни за каква било штета на телевизорот или губењето податоци што може да произлезат од нејзиното користење."
+ "Продолжи"
+ "Поставки"
+ "Се инсталираат/деинсталираат аплик. Wear"
+
diff --git a/packages/PackageInstaller/res/values-ml-television/strings.xml b/packages/PackageInstaller/res/values-ml-television/strings.xml
new file mode 100644
index 0000000000000..5ede01fe0ae73
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"
+ "പിന്നീട് നിങ്ങൾക്കിത് ക്രമീകരണവും ആപ്സും എന്നതിൽ മാറ്റാനാകും"
+ "%1$s / %2$s"
+ "സിസ്റ്റം ആപ്സ് കാണിക്കുക"
+ "ആപ്പ് അനുമതികൾ"
+ "ആപ്പ് അനുമതികൾ"
+ "%1$s അനുമതികൾ"
+ "അധിക അനുമതികൾ"
+ "%1$s അനുമതികൾ"
+
diff --git a/packages/PackageInstaller/res/values-ml-watch/strings.xml b/packages/PackageInstaller/res/values-ml-watch/strings.xml
new file mode 100644
index 0000000000000..13e38761fc23b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"
+ "%1$s / %2$s"
+ "സിസ്റ്റം ആപ്സ് കാണിക്കുക"
+ "മാറ്റാനാവില്ല"
+ "അതെ"
+ "റദ്ദാക്കുക"
+
diff --git a/packages/PackageInstaller/res/values-ml/strings.xml b/packages/PackageInstaller/res/values-ml/strings.xml
new file mode 100644
index 0000000000000..9fadd243c03c9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ml/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "പാക്കേജ് ഇൻസ്റ്റാളർ"
+ "അടുത്തത്"
+ "ഇൻസ്റ്റാളുചെയ്യുക"
+ "പൂർത്തിയായി"
+ "റദ്ദാക്കുക"
+ "ഇൻസ്റ്റാൾ ചെയ്യുന്നു..."
+ "%1$s ഇൻസ്റ്റാൾ ചെയ്യുന്നു…"
+ "അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തു."
+ "ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്യണോ? ഇതിന് ഇവയിലേക്ക് ആക്സസ്സ് ലഭിക്കും:"
+ "നിങ്ങൾക്ക് ഈ അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്യണോ? ഇത് പ്രത്യേക ആക്സസ്സൊന്നും ആവശ്യമില്ല."
+ "നിലവിലുള്ള ഈ അപ്ലിക്കേഷന് ഒരു അപ്ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്ടപ്പെടില്ല. അപ്ഡേറ്റുചെയ്ത അപ്ലിക്കേഷന് ഇവയിലേക്ക് ആക്സസ്സ് ലഭിക്കും:"
+ "ഈ അന്തർ നിർമ്മിത അപ്ലിക്കേഷന് ഒരു അപ്ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്ടപ്പെടില്ല. അപ്ഡേറ്റുചെയ്ത അപ്ലിക്കേഷന് ഇവയിലേക്ക് ആക്സസ്സ് ലഭിക്കും:"
+ "നിലവിലുള്ള ഈ അപ്ലിക്കേഷന് ഒരു അപ്ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്ടപ്പെടില്ല. ഇതിന് പ്രത്യേക ആക്സസ്സൊന്നും ആവശ്യമില്ല."
+ "ഈ അന്തർ നിർമ്മിത അപ്ലിക്കേഷന് ഒരു അപ്ഡേറ്റ് ഇൻസ്റ്റാളുചെയ്യണോ? നിങ്ങളുടെ നിലവിലെ ഡാറ്റ നഷ്ടപ്പെടില്ല. ഇതിന് പ്രത്യേക ആക്സസ്സൊന്നും ആവശ്യമില്ല."
+ "അപ്ലിക്കേഷൻ ഇൻസ്റ്റാളുചെയ്തില്ല."
+ "ഇൻസ്റ്റാൾ ചെയ്യുന്നതിൽ നിന്നും പാക്കേജിനെ തടഞ്ഞു."
+ "നിലവിലുള്ള ഒരു പാക്കേജുമായി പാക്കേജിന് പൊരുത്തക്കേടുള്ളതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."
+ "നിങ്ങളുടെ ടാബ്ലെറ്റുമായി അനുയോജ്യത ഇല്ലാത്തതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."
+ "നിങ്ങളുടെ ടിവിയ്ക്ക് ഈ ആപ്പ് അനുയോജ്യമല്ല."
+ "നിങ്ങളുടെ ഫോണുമായി അനുയോജ്യത ഇല്ലാത്തതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."
+ "പാക്കേജ് അസാധുവായി കാണപ്പെടുന്നതിനാൽ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്തില്ല."
+ "നിങ്ങളുടെ ടാബ്ലെറ്റിൽ %1$s ഇൻസ്റ്റാളുചെയ്യാനായില്ല."
+ "%1$s, നിങ്ങളുടെ ടിവിയിൽ ഇൻസ്റ്റാളുചെയ്യാനായില്ല."
+ "നിങ്ങളുടെ ഫോണിൽ %1$s ഇൻസ്റ്റാളുചെയ്യാനായില്ല."
+ "തുറക്കുക"
+ "അജ്ഞാത ഉറവിടങ്ങളിൽ നിന്ന് സ്വന്തമാക്കിയ ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ അഡ്മിനിസ്ട്രേറ്റർ അനുവദിക്കുന്നില്ല"
+ "ഈ ഉപയോക്താവിന്, തിരിച്ചറിയാനാകാത്ത ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"
+ "ആപ്പുകൾ ഇൻസ്റ്റാൾ ചെയ്യുന്നതിന് ഈ ഉപയോക്താവിന് അനുവാദമില്ല"
+ "ശരി"
+ "അപ്ലിക്കേഷനുകൾ നിയന്ത്രിക്കുക"
+ "പരിധി കഴിഞ്ഞു"
+ "%1$s ഇൻസ്റ്റാളുചെയ്യാനായില്ല. കുറച്ച് ഇടം ശൂന്യമാക്കിയതിനുശേഷം വീണ്ടും ശ്രമിക്കുക."
+ "അപ്ലിക്കേഷൻ കണ്ടെത്തിയില്ല"
+ "ഇൻസ്റ്റാളുചെയ്ത അപ്ലിക്കേഷനുകളുടെ ലിസ്റ്റിൽ അപ്ലിക്കേഷൻ കണ്ടെത്തിയില്ല."
+ "അനുവദിച്ചിട്ടില്ല"
+ "ഈ അൺഇൻസ്റ്റലേഷൻ നിർവഹിക്കാൻ നിലവിലെ ഉപയോക്താവിനെ അനുവദിച്ചിട്ടില്ല."
+ "പിശക്"
+ "ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിഞ്ഞില്ല."
+ "അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യുക"
+ "അപ്ഡേറ്റ് അൺഇൻസ്റ്റാളുചെയ്യുക"
+ "%1$s എന്നത് ഇനിപ്പറയുന്ന അപ്ലിക്കേഷന്റെ ഭാഗമാണ്:"
+ "ഈ അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യണോ?"
+ "ഈ അപ്ലിക്കേഷൻ ""എല്ലാ"" ഉപയോക്താക്കൾക്കുമായി അൺഇൻസ്റ്റാളുചെയ്യണോ? ഉപകരണത്തിലെ ""എല്ലാ"" ഉപയോക്താക്കളിൽ നിന്നും അപ്ലിക്കേഷനും അതിന്റെ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും."
+ "%1$s എന്ന ഉപയോക്താവിനായി ഈ അപ്ലിക്കേഷൻ അൺഇൻസ്റ്റാളുചെയ്യണോ?"
+ "ഫാക്ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പ് മാറ്റിസ്ഥാപിക്കണോ? എല്ലാ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും."
+ "ഫാക്ടറി പതിപ്പ് ഉപയോഗിച്ച് ഈ ആപ്പ് മാറ്റിസ്ഥാപിക്കണോ? എല്ലാ ഡാറ്റയും നീക്കംചെയ്യപ്പെടും. ഔദ്യോഗിക പ്രൊഫൈലുകൾ ഉള്ളവർ ഉൾപ്പെടെ, ഈ ഉപകരണത്തിന്റെ എല്ലാ ഉപയോക്താക്കളെയും ഇത് ബാധിക്കുന്നു."
+ "അൺ ഇൻസ്റ്റാൾ ചെയ്തുകൊണ്ടിരിക്കുന്നവ"
+ "അൺ ഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയാഞ്ഞവ"
+ "അൺഇൻസ്റ്റാൾ ചെയ്യുന്നു..."
+ "%1$s അൺഇൻസ്റ്റാൾ ചെയ്യുന്നു…"
+ "അൺഇൻസ്റ്റാളുചെയ്യൽ പൂർത്തിയായി."
+ "%1$s അൺഇൻസ്റ്റാൾ ചെയ്തു"
+ "അൺഇൻസ്റ്റാളുചെയ്തു."
+ "%1$s അൺഇൻസ്റ്റാൾ ചെയ്യൽ പരാജയം."
+ "സജീവ ഉപകരണ അഡ്മിൻ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"
+ "%1$s എന്ന ഉപയോക്താവിന്റെ സജീവ ഉപകരണ അഡ്മിൻ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്യാൻ കഴിയില്ല"
+ "ചില ഉപയോക്താക്കൾക്കോ പ്രൊഫൈലുകൾക്കോ ഈ ആപ്പ് ആവശ്യമാണ്, മറ്റുള്ളവർക്ക് ഈ ആപ്പ് അൺഇൻസ്റ്റാൾ ചെയ്തിരിക്കുന്നു"
+ "നിങ്ങളുടെ ഔദ്യോഗിക പ്രൊഫൈലിന് ഈ ആപ്പ് ആവശ്യമുള്ളതിനാൽ അത് അൺഇൻസ്റ്റാൾ ചെയ്യാനാവില്ല."
+ "നിങ്ങളുടെ ഉപകരണ അഡ്മിനിസ്ട്രേറ്ററിന് ഈ അപ്ലിക്കേഷൻ ആവശ്യമുള്ളതിനാൽ ഇത് അൺഇൻസ്റ്റാൾ ചെയ്യാനാവില്ല."
+ "ഉപകരണ അഡ്മിൻ ആപ്പുകളെ മാനേജുചെയ്യുക"
+ "ഉപയോക്താക്കളെ മാനേജുചെയ്യുക"
+ "%1$s അൺഇൻസ്റ്റാളുചെയ്യാനായില്ല."
+ "പാക്കേജ് പാഴ്സുചെയ്യുന്നതിൽ ഒരു പ്രശ്നമുണ്ടായിരുന്നു."
+ "പുതിയത്"
+ "എല്ലാം"
+ "സ്വകാര്യത"
+ "ഉപകരണ ആക്സസ്സ്"
+ "ഈ അപ്ഡേറ്റിന് പുതിയ അനുമതികളൊന്നും ആവശ്യമില്ല."
+ "നിരസിക്കുക"
+ "കൂടുതൽ വിവരങ്ങള്"
+ "എന്തായാലും നിരസിക്കുക"
+ "%1$s / %2$s"
+ "%2$s <b>%1$s</b> ആപ്പിനെ അനുവദിക്കണോ?"
+ "<b>%1$s</b> എന്നതിനെ എല്ലായ്പ്പോഴും %2$s എന്നതിന് അനുവദിക്കണമോ?"
+ "ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"
+ "എല്ലായ്പ്പോഴും"
+ "നിരസിക്കുക, വീണ്ടും ആവശ്യപ്പെടരുത്"
+ "%1$d പ്രവർത്തനരഹിതമാക്കി"
+ "എല്ലാം പ്രവർത്തനരഹിതമാക്കി"
+ "ഒന്നും പ്രവർത്തനരഹിതമാക്കിയിട്ടില്ല"
+ "അനുവദിക്കുക"
+ "ആപ്സ്"
+ "ആപ്പ് അനുമതികൾ"
+ "വീണ്ടും ആവശ്യപ്പെടരുത്"
+ "അനുമതികൾ ഇല്ല"
+ "അധിക അനുമതികൾ"
+ "ആപ്പ് വിവരം തുറക്കുക"
+
+ %1$d എണ്ണം കൂടി
+ %1$d എണ്ണം കൂടി
+
+ "ഈ ആപ്പ് Android-ന്റെ പഴയ പതിപ്പിനായാണ് രൂപകൽപ്പന ചെയ്തിരിക്കുന്നത്. അനുമതി നിരസിക്കുന്നത് തുടർന്ന് ഉദ്ദേശിച്ചവിധം പ്രവർത്തിക്കാതിരിക്കാനിടയാക്കുന്നു."
+ "ഒരു അജ്ഞാതപ്രവർത്തനം നടത്തുക"
+ "%1$d / %2$d ആപ്പുകൾ അനുവദനീയം"
+ "സിസ്റ്റം കാണിക്കുക"
+ "സിസ്റ്റം മറയ്ക്കുക"
+ "ആപ്സ് ഒന്നുമില്ല"
+ "ലൊക്കേഷൻ ക്രമീകരണം"
+ "ഈ ഉപകരണത്തിനായുള്ള ലൊക്കേഷൻ സേവനങ്ങളുടെ ദാതാവ് %1$s ആണ്. ലൊക്കേഷൻ ക്രമീകരണത്തിൽ നിന്ന് ലൊക്കേഷൻ ആക്സസ് പരിഷ്കരിക്കാവുന്നതാണ്."
+ "നിങ്ങൾ ഈ അനുമതി നിഷേധിക്കുന്നുവെങ്കിൽ, നിങ്ങളുടെ ഉപകരണത്തിന്റെ അടിസ്ഥാന ഫീച്ചറുകൾ ഉദ്ദേശിച്ചത് പോലെ തുടർന്ന് പ്രവർത്തിച്ചേക്കില്ല."
+ "നയം മുഖേനെ നടപ്പിലാക്കിയത്"
+ "നയം അനുസരിച്ച് ബാക്ക്ഗ്രൗണ്ട് ആക്സസ് പ്രവർത്തനരഹിതമാക്കി"
+ "നയം അനുസരിച്ച് ബാക്ക്ഗ്രൗണ്ട് ആക്സസ് പ്രവർത്തനക്ഷമമാക്കി"
+ "നയം അനുസരിച്ച് ഫോർഗ്രൗണ്ട് ആക്സസ് പ്രവർത്തനക്ഷമമാക്കി"
+ "അഡ്മിൻ നിയന്ത്രിക്കുന്നത്"
+
+
+
+ "എല്ലായ്പ്പോഴും"
+ "ആപ്പ് ഉപയോഗിക്കുമ്പോൾ മാത്രം"
+ "ഒരിക്കലും"
+ "ലോഡുചെയ്യുന്നു..."
+ "എല്ലാ അനുമതികളും"
+ "മറ്റ് ആപ്പ് ശേഷികൾ"
+ "അനുമതി അഭ്യർത്ഥന"
+ "സ്ക്രീൻ ഓവർലേ കണ്ടെത്തി"
+ "ഈ അനുമതി ക്രമീകരണം മാറ്റുന്നതിന്, ക്രമീകരണം > ആപ്സ് എന്നതിൽ നിന്ന് നിങ്ങളാദ്യം സ്ക്രീൻ ഓവർലേ ഓഫാക്കേണ്ടതാണ്"
+ "ക്രമീകരണം തുറക്കുക"
+ "Android Wear"
+ "ഇൻസ്റ്റാളോ അൺഇൻസ്റ്റാളോ ചെയ്യുന്നതിന് Wear-ൽ പിന്തുണയില്ല"
+ "എന്തൊക്കെ ആക്സസ്സ് ചെയ്യാനാണ് <b>%1$s</b> ആപ്പിനെ അനുവദിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക"
+ "<b>%1$s</b> അപ്ഡേറ്റ് ചെയ്തിരിക്കുന്നു. എന്തൊക്കെ ആക്സസ്സ് ചെയ്യാനാണ് ഈ ആപ്പിനെ അനുവദിക്കേണ്ടതെന്ന് തിരഞ്ഞെടുക്കുക."
+ "റദ്ദാക്കുക"
+ "തുടരുക"
+ "പുതിയ അനുമതികൾ"
+ "നിലവിലെ അനുമതികൾ"
+ "ആപ്പ് തയ്യാറാക്കുന്നു…"
+ "അജ്ഞാതം"
+ "നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ടാബ്ലെറ്റ് അനുവദിക്കപ്പെടില്ല."
+ "നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ടിവി അനുവദിക്കപ്പെടില്ല."
+ "നിങ്ങളുടെ സുരക്ഷയ്ക്ക്, ഈ ഉറവിടത്തിൽ നിന്നുള്ള, തിരിച്ചറിയാനാകാത്ത ആപ്സ് ഇൻസ്റ്റാൾ ചെയ്യാൻ നിങ്ങളുടെ ഫോൺ അനുവദിക്കപ്പെടില്ല."
+ "തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ഫോണും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ഫോണിന് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."
+ "തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ടാബ്ലെറ്റും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടാബ്ലെറ്റിന് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."
+ "തിരിച്ചറിയാനാകാത്ത ആപ്പുകളാൽ നിങ്ങളുടെ ടിവിയും വ്യക്തിഗത ഡാറ്റയും ആക്രമിക്കപ്പെടാനുള്ള സാധ്യത വളരെ കൂടുതലാണ്. ഈ ആപ്പ് ഇൻസ്റ്റാൾ ചെയ്യുന്നതിലൂടെ, ഇത് ഉപയോഗിക്കുന്നതിനാൽ നിങ്ങളുടെ ടിവിക്ക് സംഭവിച്ചേക്കാവുന്ന എല്ലാ നാശനഷ്ടങ്ങൾക്കും അല്ലെങ്കിൽ ഡാറ്റാ നഷ്ടങ്ങൾക്കും നിങ്ങൾക്കാണ് ഉത്തരവാദിത്തമെന്ന് നിങ്ങൾ അംഗീകരിക്കുന്നു."
+ "തുടരുക"
+ "ക്രമീകരണം"
+ "Wear ആപ്പുകൾ ഇൻസ്റ്റാൾ/അൺ ഇൻസ്റ്റാൾ ചെയ്യൽ"
+
diff --git a/packages/PackageInstaller/res/values-mn-television/strings.xml b/packages/PackageInstaller/res/values-mn-television/strings.xml
new file mode 100644
index 0000000000000..39c899f987839
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Taтгалзаад дахин бүү асуугаарай"
+ "Та дараа үүнийг Toхиргоо & Апп дотроос солих боломжтой"
+ "%1$s / %2$s"
+ "Системийн аппыг харуулах"
+ "Апп-н зөвшөөрөл"
+ "Апп-н зөвшөөрөл"
+ "%1$s зөвшөөрөл"
+ "Нэмэлт зөвшөөрөл"
+ "%1$s зөвшөөрөл"
+
diff --git a/packages/PackageInstaller/res/values-mn-watch/strings.xml b/packages/PackageInstaller/res/values-mn-watch/strings.xml
new file mode 100644
index 0000000000000..120c336f02125
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Татгалзах, дахин бүү асуугаарай"
+ "%1$s / %2$s"
+ "Системийн аппыг харуулах"
+ "Өөрчлөх боломжгүй"
+ "Тийм"
+ "Цуцлах"
+
diff --git a/packages/PackageInstaller/res/values-mn/strings.xml b/packages/PackageInstaller/res/values-mn/strings.xml
new file mode 100644
index 0000000000000..1fd12a247af5c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mn/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Багц суулгагч"
+ "Дараах"
+ "Суулгах"
+ "Дуусгах"
+ "Цуцлах"
+ "Суулгаж байна…"
+ "%1$s-г суулгаж байна…"
+ "Апп суулгагдсан."
+ "Та энэ аппликешныг суулгамаар байна уу? Энэ дараахад хандах болно:"
+ "Та энэ аппликешныг суулгах уу? Энэ тусгай хандалт шаардахгүй."
+ "Та энэ аппликейшны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Шинэчлэгдсэн аппликейшн нь дараахад хандаж чадна:"
+ "Та энэ үндсэн аппликейшны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Шинэчлэгдсэн аппликейшн нь дараахад хандаж чадна:"
+ "Та энэ аппликешны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Энэ ямар нэгэн тусгай эрх шаардахгүй."
+ "Та энэ үндсэн аппликешны шинэчлэлтийг суулгах уу? Таны хуучин дата устах болно. Энэ ямар нэгэн тусгай эрх шаардахгүй."
+ "Апп суулгагдаагүй."
+ "Багц суулгахыг блоклосон байна."
+ "Багц одоогийн багцтай тохирохгүй байгаа тул апп-г суулгаж чадсангүй."
+ "Апп таны таблеттай тохирохгүй байгаа тул үүнийг суулгасангүй."
+ "Энэ апп нь таны ТВ-д нийцэхгүй."
+ "Апп таны утсанд тохирохгүй байгаа тул үүнийг суулгасангүй."
+ "Багц хүчингүй тул апп-г суулгасангүй."
+ "%1$s таны таблет дээр суусангүй."
+ "%1$s нь таны телевизэд суурилуулах боломжгүй байна."
+ "%1$s-г таны утсан дээр суулгах боломжгүй."
+ "Нээх"
+ "Таны админ тодорхойгүй сурвалжаас татсан апп суулгахыг зөвшөөрдөггүй"
+ "Энэ хэрэглэгч тодорхойгүй апп суулгах боломжгүй байна"
+ "Энэ хэрэглэгч апп суулгах зөвшөөрөлгүй байна"
+ "ОК"
+ "Апп удирдах"
+ "Зай дутагдаж байна"
+ "%1$s-г суулгаж чадсангүй. Зайг чөлөөлөөд дахин оролдоно уу."
+ "Апп олдсонгүй"
+ "Суулгасан апп-н жагсаалт дотроос апп олдсонгүй."
+ "Зөвшөөрөөгүй"
+ "Одоогийн хэрэглэгч үүнийг устгах боломжгүй."
+ "Алдаа"
+ "Апп-г устгаж чадсангүй."
+ "Апп устгах"
+ "Шинэчлэлийг устгах"
+ "%1$s нь дараах апп-н хэсэг болно:"
+ "Та энэ апп-г устгамаар байна уу?"
+ "Та энэ апп-г ""бүх"" хэрэглэгчээс устгах уу? Аппикешн болон доторх дата нь төхөөрөмж дээрх ""бүх"" хэрэглэгчээс устгагдах болно."
+ "Та энэ апп-г %1$s хэрэглэгчийн хувьд устгах уу?"
+ "Энэ апп-г үйлдвэрээс ирсэн хувилбараар нь солих уу? Бүх өгөгдөл устах болно."
+ "Энэ апп-г үйлдвэрээс ирсэн хувилбараар нь солих уу? Бүх өгөгдөл устах болно. Энэ нь ажлын профайлтай хэрэглэгч зэрэг энэ төхөөрөмжийн бүх хэрэглэгчдэд үйлчлэх болно."
+ "Устгаж байна"
+ "Устгаж чадсангүй"
+ "Устгаж байна…"
+ "%1$s-г устгаж байна…"
+ "Устгаж дуусав."
+ "%1$s-г устгасан"
+ "Устгалт амжилтгүй болов."
+ "%1$s-г устгаж чадсангүй."
+ "Идэвхтэй төхөөрөмжийн админ аппыг устгах боломжгүй"
+ "%1$s-д идэвхтэй төхөөрөмжийн админ аппыг устгах боломжгүй байна"
+ "Энэ апп нь зарим хэрэглэгч эсвэл профайлд шаардлагатай учир үүнийг тэдгээрээс бусад хэрэглэгчдээс устгасан"
+ "Энэ апп таны профайлд шаардлагатай бөгөөд устгах боломжгүй."
+ "Энэ апп нь таны төхөөрөмжийн админд шаардлагатай бөгөөд устгах боломжгүй."
+ "Төхөөрөмжийн админ аппыг удирдах"
+ "Хэрэгчлэгчдийг удирдах"
+ "%1$s-г устгаж чадсангүй."
+ "Багцийг задлахад алдаа гарав."
+ "Шинэ"
+ "Бүгд"
+ "Нууцлал"
+ "Төхөөрөмжид хандах"
+ "Энэ шинэчлэл шинэ зөвшөөрөл шаардахгүй."
+ "Татгалзах"
+ "Дэлгэрэнгүй мэдээлэл"
+ "Хэдийд ч татгалзах"
+ "%2$s-ийн %1$s"
+ "<b>%1$s</b>-г %2$s-г зөвшөөрөх үү?"
+ "<b>%1$s</b>-г %2$s-д байнга зөвшөөрөх үү?"
+ "Зөвхөн апп ашиглах үед"
+ "Байнга"
+ "Taтгалзаад дахин бүү асуугаарай"
+ "%1$d-г цуцалсан"
+ "бүгдийг цуцалсан"
+ "алийг ч цуцлаагүй"
+ "Зөвшөөрөх"
+ "Апп"
+ "Апп зөвшөөрөл"
+ "Дахиж бүү асуу"
+ "Зөвшөөрөлгүй байна"
+ "Нэмэлт зөвшөөрөл"
+ "Аппын мэдээллийг нээх"
+
+ %1$d бусад
+ %1$d бусад
+
+ "Энэхүү аппыг нь Android-ын хуучин хувилбарт зориулсан. Зөвшөөрлийг үгүйсгэх нь цаашид ажиллахгүй болгож болно."
+ "Танигдаагүй үйлдлийг гүйцэтгэх"
+ "%2$d-с %1$d аппыг зөвшөөрдөг"
+ "Системийг харуулах"
+ "Системийг нуух"
+ "Апп байхгүй"
+ "Байршлын тохиргоо"
+ "%1$s нь энэ төхөөрөмжийн байршлын үйлчилгээ үзүүлэгч юм. Байршилд хандалтыг байршлын тохиргоо хэсгээс өөрчилж болно."
+ "Хэрэв та энэ зөвшөөрөлд татгалзсан тохиолдолд таны төхөөрөмжийн үндсэн функц нь алдаатай ажиллаж магадгүй."
+ "Бодлогын дагуу хэрэгжсэн"
+ "Арын дэвсгэрийн хандалтыг удирдамжаас идэвхгүй болгосон"
+ "Арын дэвсгэрийн хандалтыг удирдамжаас идэвхтэй болгосон"
+ "Нүүрний дэвсгэрийн хандалтыг удирдамжаас идэвхтэй болгосон"
+ "Админ удирддаг"
+
+
+
+ "Байнга"
+ "Зөвхөн апп ашиглах үед"
+ "Хэзээ ч үгүй"
+ "Ачаалж байна..."
+ "Бүх зөвшөөрөл"
+ "Бусад апп-ын боломж"
+ "Зөвшөөрлийн хүсэлт"
+ "Дэлгэцийн давхарга илрүүллээ"
+ "Зөвшөөрлийн тохиргоог өөрчлөхийн тулд, эхлээд Тохиргоо ба Апп хэсгээс дэлгэцийн давхаргыг унтраана уу."
+ "Тохиргоог нээх"
+ "Android Wear"
+ "Суулгах/Устгах үйлдлийг Wear дэмжээгүй."
+ "<b>%1$s</b>-н хандаж болох зүйлсийг сонгоно уу"
+ "<b>%1$s</b>-г шинэчиллээ. Энэ апп-н хандаж болох зүйлсийг сонгоно уу."
+ "Цуцлах"
+ "Үргэлжлүүлэх"
+ "Шинэ зөвшөөрөл"
+ "Одоогийн зөвшөөрөл"
+ "Апп-г байршуулж байна…"
+ "Тодорхойгүй"
+ "Таны аюулгүй байдлыг хангахын тулд таны таблет энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."
+ "Таны аюулгүй байдлыг хангахын тулд таны ТВ энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."
+ "Таны аюулгүй байдлыг хангахын тулд таны утас энэ эх сурвалжаас тодорхойгүй апп суулгахыг зөвшөөрдөггүй."
+ "Таны утас болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны утсанд гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."
+ "Таны таблет болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны таблетад гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."
+ "Таны ТВ болон хувийн өгөгдөл тодорхойгүй апп суулгасан тохиолдолд гэмтэж болзошгүй. Энэ аппыг суулгаснаар үүнийг ашигласнаас үүдэн таны ТВ-д гэмтэл гарах, эсвэл өгөгдөл устах зэрэг эрсдэлийг хариуцна гэдгээ зөвшөөрч байна."
+ "Үргэлжлүүлэх"
+ "Тохиргоо"
+ "Зүүсгэл аппыг суулгаж/устгаж байна"
+
diff --git a/packages/PackageInstaller/res/values-mr-television/strings.xml b/packages/PackageInstaller/res/values-mr-television/strings.xml
new file mode 100644
index 0000000000000..e648404807f5d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "नकार द्या आणि पुन्हा विचारू नका"
+ "तुम्ही हे नंतर सेटिंग्ज आणि अॅप्स मध्ये बदलू शकता"
+ "%1$s / %2$s"
+ "सिस्टम अॅप्स दर्शवा"
+ "अॅप परवानग्या"
+ "अॅप परवानग्या"
+ "%1$s परवानग्या"
+ "अतिरिक्त परवानग्या"
+ "%1$s परवानग्या"
+
diff --git a/packages/PackageInstaller/res/values-mr-watch/strings.xml b/packages/PackageInstaller/res/values-mr-watch/strings.xml
new file mode 100644
index 0000000000000..74d32df7e37ba
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "नकार द्या, पुन्हा विचारू नका"
+ "%1$s / %2$s"
+ "सिस्टम अॅप्स दर्शवा"
+ "बदलू शकत नाही"
+ "होय"
+ "रद्द करा"
+
diff --git a/packages/PackageInstaller/res/values-mr/strings.xml b/packages/PackageInstaller/res/values-mr/strings.xml
new file mode 100644
index 0000000000000..b9acaa5c39067
--- /dev/null
+++ b/packages/PackageInstaller/res/values-mr/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "पॅकेज स्थापनकर्ता"
+ "पुढील"
+ "स्थापित करा"
+ "पूर्ण झाले"
+ "रद्द करा"
+ "इंस्टॉल करत आहे..."
+ "%1$s इन्स्टॉल करत आहे…"
+ "अॅप इंस्टॉल झाला."
+ "तुम्ही हा अॅप्लिकेशन इंस्टॉल करू इच्छिता? यास यावर प्रवेश मिळेल:"
+ "तुम्ही हा अॅप्लिकेशन इंस्टॉल करू इच्छिता? यास कोणत्याही विशेष प्रवेशाची आवश्यकता नसते."
+ "तुम्हाला सद्य अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. अपडेट केलेल्या अॅप्लिकेशनला यावर अॅक्सेस मिळेल:"
+ "तुम्ही या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. अपडेट केलेल्या अॅप्लिकेशनला यावर अॅक्सेस मिळेल:"
+ "तुम्हाला सद्य अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. यासाठी कोणताही विशेष अॅक्सेस आवश्यक नसतो."
+ "तुम्ही या बिल्ट-इन अॅप्लिकेशनवर अपडेट इंस्टॉल करायची आहे? तुम्ही तुमचा सद्य डेटा गमावणार नाही. यासाठी कोणताही विशेष अॅक्सेस आवश्यक नसतो."
+ "अॅप इंस्टॉल झाला नाही."
+ "पॅकेेच इंस्टॉल होण्यास अवरोधित केलेले होते."
+ "पॅकेजचा विद्यमान पॅकेजशी विरोध असल्याने अॅप इंस्टॉल केला नाही."
+ "अॅप आपल्या टॅब्लेटशी सुसंगत नसल्याने अॅप इंस्टॉल केला नाही."
+ "हा अॅप आपल्या टीव्हीशी सुसंगत नाही."
+ "अॅप आपल्या फोनशी सुसंगत नसल्याने अॅप इंस्टॉल केला नाही."
+ "पॅकेज अवैध असल्याचे दिसत असल्याने अॅप इंस्टॉल केले नाही."
+ "%1$s आपल्या टॅब्लेटवर इंस्टॉल केला जाऊ शकला नाही."
+ "%1$s आपल्या टीव्हीवर इंस्टॉल केले जाऊ शकले नाही."
+ "%1$s आपल्या फोनवर इंस्टॉल केला जाऊ शकला नाही."
+ "उघडा"
+ "अज्ञात स्रोतांकडून मिळवलेल्या अॅप्सच्या स्थापनेला आपला प्रशासक अनुमती देत नाही"
+ "या वापरकर्त्याद्वारे अज्ञात अॅप्स इंस्टॉल केली जाऊ शकत नाहीत"
+ "या वापरकर्त्याला अॅप्स इंस्टॉल करण्याची परवानगी नाही"
+ "ठीक"
+ "अॅप्स व्यवस्थापित करा"
+ "स्थानाबाहेर"
+ "%1$s इंस्टॉल केला जाऊ शकला नाही. काही स्थान मोकळे करा आणि पुन्हा प्रयत्न करा."
+ "अॅप आढळला नाही"
+ "इंस्टॉल केलेल्या अॅप्सच्या सूचीमध्ये अॅप आढळला नाही."
+ "अनुमती नाही"
+ "हे अनइंस्टॉल करण्याची वर्तमान वापरकर्त्यास अनुमती नाही."
+ "एरर"
+ "अॅप अनइंस्टॉल करणे शक्य झाले नाही."
+ "अॅप अनइंस्टॉल करा"
+ "अपडेट अनइंस्टॉल करा"
+ "%1$s खालील अॅप चा भाग आहे:"
+ "तुम्ही हा अॅप अनइंस्टॉल करू इच्छिता?"
+ "तुम्ही हा अॅप ""सर्व"" वापरकर्त्यांसाठी अनइंस्टॉल करू इच्छिता? अॅप्लिकेशन आणि त्याचा डेटा डिव्हाइसवरील ""सर्व"" वापरकर्त्यांवरून काढला जाईल."
+ "तुम्ही %1$s वापरकर्त्यासाठी हा अॅप विस्थापित करु इच्छिता?"
+ "फॅक्टरी आवृत्तीसह हा अॅप पुनर्स्थित करायचा? सर्व डेटा काढला जाईल."
+ "फॅक्टरी आवृत्तीसह हा अॅप पुनर्स्थित करायचा? सर्व डेटा काढला जाईल. हे कार्य प्रोफाईल असलेल्यांसह या डिव्हाइसच्या सर्व वापरकर्त्यांना प्रभावित करते."
+ "अनइंस्टॉल करणे चालू आहे"
+ "अनइंस्टॉल करणे अयशस्वी झाले"
+ "अनइंस्टॉल करत आहे…"
+ "%1$s अनइंस्टॉल करत आहे…"
+ "अनइंस्टॉल करणे समाप्त."
+ "%1$s अनइंस्टॉल केले"
+ "अनइंस्टॉल करणे अयशस्वी."
+ "%1$s अनइंस्टॉल करणे अयशस्वी झाले."
+ "अॅक्टिव्हेट डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"
+ "%1$s साठी अॅक्टिव्हेट डिव्हाइस प्रशासक अॅप अनइंस्टॉल करू शकत नाही"
+ "हा अॅप काही वापरकर्ते किंवा प्रोफाईलसाठी आवश्यक आहे आणि इतरांसाठी अनइंस्टॉल केला होता"
+ "आपल्या प्रोफाईलसाठी हा अॅप आवश्यक आहे आणि अनइंस्टॉल केला जाऊ शकत नाही."
+ "तुमच्या डिव्हाइस प्रशासकास हे अॅप आवश्यक आहे आणि ते अनइंस्टॉल केले जाऊ शकत नाही."
+ "डिव्हाइस प्रशासक अॅप्स व्यवस्थापित करा"
+ "वापरकर्त्यांना व्यवस्थापित करा"
+ "%1$s अनइंस्टॉल केला जाऊ शकला नाही."
+ "पॅकेज चे विश्लेषण करताना समस्या आली."
+ "नवीन"
+ "सर्व"
+ "गोपनीयता"
+ "डिव्हाइस अॅक्सेस"
+ "या अद्यतनास कोणत्याही नवीन परवानग्यांची आवश्यकता नाही."
+ "नकार द्या"
+ "अधिक माहिती"
+ "तरीही नकार द्या"
+ "%2$s पैकी %1$s"
+ "<b>%1$s</b> ला %2$s ची अनुमती द्यायची?"
+ "<b>%1$s</b> ला नेहमी %2$s ची अनुमती द्यायची का?"
+ "फक्त अॅप वापरत असताना"
+ "नेहमी"
+ "नकार द्या आणि पुन्हा विचारू नका"
+ "%1$d अक्षम केल्या"
+ "सर्व अक्षम केल्या"
+ "कोणत्याही अक्षम केल्या नाहीत"
+ "अनुमती द्या"
+ "अॅप्स"
+ "अॅप परवानग्या"
+ "पुन्हा विचारू नका"
+ "परवानग्या नाहीत"
+ "अतिरिक्त परवानग्या"
+ "अॅप माहिती उघडा"
+
+ आणखी %1$d
+ आणखी %1$d
+
+ "हा अॅप Android च्या जुन्या आवृत्तीसाठी डीझाइन करण्यात आला होता. परवानगी नाकारल्यामुळे तो यापुढे उद्देशाप्रमाणे कार्य करणार नाही."
+ "अज्ञात क्रिया करा"
+ "%2$d पैकी %1$d अॅप्सना अनुमती दिली"
+ "सिस्टम दर्शवा"
+ "सिस्टीम लपवा"
+ "कोणतेही अॅप्स नाहीत"
+ "स्थान सेटिंग्ज"
+ "या डिव्हाइससाठी %1$s स्थान सेवांचा प्रदाता आहे. स्थान प्रवेश स्थान सेटिंग्ज वरून सुधारित केला जाऊ शकतो."
+ "तुम्ही ही परवानगी नाकारल्यास, आपल्या डिव्हाइसची मुलभूत वैशिष्ट्ये अपेक्षित असल्याप्रमाणे कदाचित कार्य करू शकणार नाहीत."
+ "धोरणाद्वारे सक्ती केली"
+ "धोरणाद्वारे बॅकग्राउंड अॅक्सेस बंद केला आहे"
+ "धोरणाद्वारे बॅकग्राउंड अॅक्सेस सुरू केला आहे"
+ "धोरणाद्वारे फोरग्राउंड अॅक्सेस सुरू केला आहे"
+ "प्रशासनाद्वारे नियंत्रित केलेले"
+
+
+
+ "नेहमी"
+ "फक्त अॅप वापरत असताना"
+ "कधीही नाही"
+ "लोड करत आहे..."
+ "सर्व परवानग्या"
+ "अन्य अॅप क्षमता"
+ "परवानगीची विनंती"
+ "स्क्रीन ओव्हरले आढळले"
+ "हे परवानगी सेटिंग बदलण्यासाठी, तुम्हाला सेटिंग्ज > अॅप्स मधून स्क्रीन ओव्हरले बंद करावे लागेल"
+ "सेटिंग्ज उघडा"
+ "Android Wear"
+ "इंस्टॉल करा/अनइंस्टॉल करा क्रिया Wear वर समर्थित नाहीत."
+ "<b>%1$s</b> ला कशामध्ये प्रवेश करण्याची अनुमती द्यावी ते निवडा"
+ "<b>%1$s</b> अपडेट केला गेला आहे. या अॅपला कशामध्ये प्रवेश करण्याची अनुमती द्यावी ते निवडा."
+ "रद्द करा"
+ "सुरू ठेवा"
+ "नवीन परवानग्या"
+ "वर्तमान परवानग्या"
+ "अॅप प्रारंभाच्या स्थितीत आहे..."
+ "अज्ञात"
+ "आपल्या सुरक्षिततेसाठी, आपल्या टॅबलेटला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."
+ "आपल्या सुरक्षिततेसाठी, आपल्या टीव्हीला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."
+ "आपल्या सुरक्षिततेसाठी, आपल्या फोनला या स्रोताकडील अज्ञात अॅप्स इंस्टॉल करण्याची अनुमती नाही."
+ "तुमचा फोन आणि वैयक्तिक डेटा अज्ञात अॅप्समुळे होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्स्टॉल करून, तुम्ही सहमती देता की तो वापरल्याने होणार्या तुमच्या फोनच्या कोणत्याही प्रकारच्या नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."
+ "तुमचा टॅबलेट आणि वैयक्तिक डेटा अज्ञात अॅप्समुळे होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्स्टॉल करून, तुम्ही सहमती देता की तो वापरल्याने होणार्या तुमच्या टॅबलेटच्या कोणत्याही प्रकारच्या नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."
+ "तुमचा टीव्ही आणि वैयक्तिक डेटा अज्ञात अॅप्समुळे होणार्या अटॅकमुळे अधिक असुरक्षित आहे. हा अॅप इन्स्टॉल करून, तुम्ही सहमती देता की तो वापरल्याने होणार्या तुमच्या टीव्हीच्या कोणत्याही प्रकारच्या नुकसान किंवा डेटा हानीसाठी तुम्ही जबाबदार आहात."
+ "सुरू ठेवा"
+ "सेटिंग्ज"
+ "वेअर अॅप्स इन्स्टॉल/अनइन्स्टॉल करणे"
+
diff --git a/packages/PackageInstaller/res/values-ms-television/strings.xml b/packages/PackageInstaller/res/values-ms-television/strings.xml
new file mode 100644
index 0000000000000..989aba7baf363
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Tolak dan jangan tanya lagi"
+ "Anda boleh menukar ini nanti dalam Tetapan > Apl"
+ "%1$s / %2$s"
+ "Tunjukkan apl sistem"
+ "Kebenaran apl"
+ "Kebenaran apl"
+ "Kebenaran %1$s"
+ "Kebenaran tambahan"
+ "Kebenaran %1$s"
+
diff --git a/packages/PackageInstaller/res/values-ms-watch/strings.xml b/packages/PackageInstaller/res/values-ms-watch/strings.xml
new file mode 100644
index 0000000000000..dad185fab4bb8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Tolak, jangan tanya lagi"
+ "%1$s / %2$s"
+ "Tunjukkan apl sistem"
+ "Tidak dpt diubah"
+ "Ya"
+ "Batal"
+
diff --git a/packages/PackageInstaller/res/values-ms/strings.xml b/packages/PackageInstaller/res/values-ms/strings.xml
new file mode 100644
index 0000000000000..6ab23acaecf0c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ms/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pemasang pakej"
+ "Seterusnya"
+ "Pasang"
+ "Selesai"
+ "Batal"
+ "Memasang..."
+ "Memasang %1$s…"
+ "Aplikasi dipasang."
+ "Adakah anda mahu memasang aplikasi ini? Aplikasi ini akan mendapat akses kepada:"
+ "Adakah anda mahu memasang aplikasi ini? Aplikasi ini tidak memerlukan sebarang akses khas."
+ "Adakah anda mahu memasang kemas kini kepada aplikasi sedia ada ini? Data sedia ada anda tidak akan hilang. Aplikasi yang dikemaskinikan akan mendapat akses kepada:"
+ "Adakah anda ingin memasang kemas kini kepada aplikasi terbina dalam ini? Data sedia ada anda tidak akan hilang. Aplikasi yang dikemaskinikan akan mendapat akses kepada:"
+ "Adakah anda mahu memasang kemas kini untuk aplikasi sedia ada ini? Data sedia ada anda tidak akan hilang. Hal ini tidak memerlukan sebarang akses khas."
+ "Adakah anda mahu memasang kemas kini untuk aplikasi terbina dalam ini? Data sedia ada anda tidak akan hilang. Hal ini tidak memerlukan sebarang akses khas."
+ "Aplikasi tidak dipasang."
+ "Pakej ini telah disekat daripada dipasang."
+ "Apl tidak dipasang kerana pakej bercanggah dengan pakej yang sedia ada."
+ "Apl tidak dipasang kerana apl tidak serasi dengan tablet anda."
+ "Apl ini tidak serasi dengan TV anda."
+ "Apl tidak dipasang kerana apl tidak serasi dengan telefon anda."
+ "Apl tidak dipasang kerana pakej tidak sah."
+ "%1$s tidak dapat dipasangkan pada tablet anda."
+ "%1$s tidak boleh dipasang pada TV anda."
+ "%1$s tidak dapat dipasangkan pada telefon anda."
+ "Buka"
+ "Pentadbir anda tidak membenarkan pemasangan apl yang diperoleh daripada sumber yang tidak diketahui"
+ "Apl yang tidak diketahui tidak boleh dipasang oleh pengguna ini"
+ "Pengguna ini tidak dibenarkan memasang apl"
+ "OK"
+ "Urus aplikasi"
+ "Kehabisan ruang"
+ "%1$s tidak dapat dipasang. Kosongkan sebahagian ruang dan cuba lagi."
+ "Aplikasi tidak ditemui"
+ "Aplikasi tidak ditemui dalam senarai aplikasi yang dipasang."
+ "Tidak dibenarkan"
+ "Pengguna semasa tidak dibenarkan untuk melaksanakan penyahpasangan ini."
+ "Ralat"
+ "Apl tidak dapat dinyapasang."
+ "Nyahpasang aplikasi"
+ "Nyahpasang kemas kini"
+ "%1$s merupakan sebahagian daripada aplikasi berikut:"
+ "Adakah anda mahu menyahpasang aplikasi ini?"
+ "Adakah anda mahu menyahpasang apl ini untuk ""semua"" pengguna? Aplikasi dan datanya akan dialih keluar daripada ""semua"" pengguna pada peranti."
+ "Adakah anda ingin menyahpasang apl ini untuk pengguna %1$s?"
+ "Gantikan apl ini dengan versi kilang? Semua data akan dialih keluar."
+ "Gantikan apl ini dengan versi kilang? Semua data akan dialih keluar. Tindakan ini melibatkan semua pengguna peranti ini, termasuk mereka yang mempunyai profil kerja."
+ "Penyahpasangan yang sedang berjalan"
+ "Penyahpasangan yang gagal"
+ "Menyahpasang..."
+ "Menyahpasang %1$s…"
+ "Nyahpasang selesai."
+ "%1$s dinyahpasang"
+ "Nyahpasang tidak berjaya."
+ "Tidak berjaya menyahpasang %1$s."
+ "Tidak dapat menyahpasang apl pentadbir peranti yang aktif"
+ "Tidak dapat menyahpasang apl pentadbir peranti yang aktif untuk %1$s"
+ "Apl ini diperlukan untuk sesetengah pengguna atau profil dan telah dinyahpasang untuk yang lain"
+ "Apl ini diperlukan untuk profil anda dan tidak boleh dinyahpasang."
+ "Apl ini diperlukan oleh pentadbir peranti anda dan tidak boleh dinyahpasang."
+ "Urus apl pentadbir peranti"
+ "Urus pengguna"
+ "%1$s tidak dapat dinyahpasang."
+ "Terdapat masalah menghuraikan pakej."
+ "Baharu"
+ "Semua"
+ "Privasi"
+ "Akses Peranti"
+ "Kemas kini ini tidak memerlukan kebenaran baharu."
+ "Tolak"
+ "Maklumat lanjut"
+ "Tolak juga"
+ "%1$s daripada %2$s"
+ "Benarkan <b>%1$s</b> %2$s?"
+ "Sentiasa benarkan <b>%1$s</b> %2$s?"
+ "Hanya semasa menggunakan apl"
+ "Sentiasa"
+ "Tolak dan jangan tanya lagi"
+ "%1$d dilumpuhkan"
+ "semua dilumpuhkan"
+ "tiada apa-apa yang dilumpuhkan"
+ "Benarkan"
+ "Apl"
+ "Kebenaran apl"
+ "Jangan tanya lagi"
+ "Tiada kebenaran"
+ "Kebenaran tambahan"
+ "Buka maklumat apl"
+
+ %1$d lagi
+ %1$d lagi
+
+ "Apl ini direka bentuk untuk versi Android yang lebih lama. Tindakan menafikan kebenaran boleh menyebabkannya tidak berfungsi seperti yang dimaksudkan lagi."
+ "laksanakan tindakan yang tidak diketahui"
+ "%1$d daripada %2$d apl dibenarkan"
+ "Tunjukkan sistem"
+ "Sembunyikan sistem"
+ "Tiada apl"
+ "Tetapan Lokasi"
+ "%1$s ialah pembekal perkhidmatan lokasi untuk peranti ini. Akses lokasi boleh diubah suai daripada tetapan lokasi."
+ "Jika anda tolak kebenaran ini, ciri asas peranti anda mungkin tidak berfungsi seperti yang dimaksudkan lagi."
+ "Dikuatkuasakan oleh dasar"
+ "Akses latar belakang dilumpuhkan oleh dasar"
+ "Akses latar belakang didayakan oleh dasar"
+ "Akses latar depan didayakan oleh dasar"
+ "Dikawal oleh pentadbir"
+
+
+
+ "Sentiasa"
+ "Hanya semasa menggunakan apl"
+ "Jangan sekali-kali"
+ "Memuatkan…"
+ "Semua kebenaran"
+ "Keupayaan apl yang lain"
+ "Permintaan kebenaran"
+ "Tindanan skrin dikesan"
+ "Untuk menukar tetapan kebenaran ini, anda perlu mematikan tindanan skrin daripada Tetapan > Apl terlebih dahulu"
+ "Buka tetapan"
+ "Android Wear"
+ "Tindakan pasang/nyahpasang tidak disokong pada Wear."
+ "Pilih perkara yang boleh diakses oleh <b>%1$s</b>"
+ "<b>%1$s</b> telah dikemas kini. Pilih perkara yang boleh diakses oleh apl ini."
+ "Batal"
+ "Teruskan"
+ "Kebenaran baharu"
+ "Kebenaran semasa"
+ "Pemeringkatan apl…"
+ "Tidak diketahui"
+ "Untuk keselamatan, tablet anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."
+ "Untuk keselamatan, TV anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."
+ "Untuk keselamatan, telefon anda tidak dibenarkan memasang apl yang tidak diketahui daripada sumber ini."
+ "Telefon dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada telefon anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."
+ "Tablet dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada tablet anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."
+ "TV dan data peribadi anda lebih mudah diserang oleh apl yang tidak diketahui. Dengan memasang apl ini, anda bersetuju bahawa anda bertanggungjawab atas sebarang kerosakan pada TV anda atau kehilangan data yang mungkin disebabkan oleh penggunaan apl tersebut."
+ "Teruskan"
+ "Tetapan"
+ "Memasang/menyahpasang apl wear"
+
diff --git a/packages/PackageInstaller/res/values-my-television/strings.xml b/packages/PackageInstaller/res/values-my-television/strings.xml
new file mode 100644
index 0000000000000..b802f5932490b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ငြင်းဆိုပြီး ထပ်မံ မမေးပါနှင့်"
+ "နောင်တွင် ဤသည်အား ဆက်တင်များ > အက်ပ်များတွင် ပြင်နိုင်၏"
+ "%1$s / %2$s"
+ "စနစ်အပ်ဖ်များ ပြသရန်"
+ "အက်ပ်ခွင့်ပြုချက်များ"
+ "အက်ပ်ခွင့်ပြုချက်များ"
+ "%1$s ခွင့်ပြုချက်များ"
+ "အပိုဆောင်း ခွင့်ပြုချက်များ"
+ "%1$s ခွင့်ပြုချက်များ"
+
diff --git a/packages/PackageInstaller/res/values-my-watch/strings.xml b/packages/PackageInstaller/res/values-my-watch/strings.xml
new file mode 100644
index 0000000000000..21283c036fafb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ငြင်းပယ်သည်၊ ထပ်မမေးပါနှင့်"
+ "%1$s / %2$s"
+ "စနစ်အပ်ဖ်များ ပြသရန်"
+ "ပြောင်းလဲ မရနိုင်ပါ"
+ "Yes"
+ "မလုပ်တော့"
+
diff --git a/packages/PackageInstaller/res/values-my/strings.xml b/packages/PackageInstaller/res/values-my/strings.xml
new file mode 100644
index 0000000000000..02199c35958a0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-my/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Package ထည့်သွင်းခြင်း"
+ "ရှေ့သို့"
+ "ထည့်သွင်းပါ"
+ "ပြီးပါပြီ"
+ "မလုပ်တော့"
+ "ထည့်သွင်းနေပါသည်"
+ "%1$s ကို ထည့်သွင်းနေသည်…"
+ "အက်ပ်ထည့်သွင်းပြီး"
+ "ဤအပလီကေးရှင်းကို ထည့်သွင်းပါမလား။ ဤအပလီကေးရှင်း သုံးစွဲခွင့်ရှိမှာ ကတော့:"
+ "ဤအပလီကေးရှင်းကို ထည့်သွင်းပါမလား။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"
+ "လက်ရှိ ရှိပြီးသား အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။ ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အပ်ဒိတ်လုပ်လိုက်သော အပလီကေးရှင်းသုံးစွဲခွင့်ရှိမှာ များကတော့-"
+ "အဆင့်သင့် ပါလာသော အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။ ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အပ်ဒိတ်လုပ်လိုက်သော အပလီကေးရှင်း သုံးစွဲခွင့်ရှိမှာ များကတော့ -"
+ "လက်ရှိ ရှိပြီးသား အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။ ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"
+ "အဆင့်သင့် ပါလာသော အပလီကေးရှင်းကို အပ်ဒိတ်လုပ်လိုပါသလား။ ရှိပြီးသား အချက်အလက်များကို ဆုံးရှုံးမည် မဟုတ်ပါ။ အထူးတလည် သုံးခွင့် မလိုအပ်ပါ"
+ "အက်ပ်မထည့်သွင်းရသေးပါ"
+ "ပက်ကေ့ထည့်သွင်းခြင်းကို ပိတ်ဆို့ထားသည်။"
+ "ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် လက်ရှိပက်ကေ့ဂျ်နှင့် တိုက်နေသည်။"
+ "အက်ပ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် သင့်တက်ဘလက်နှင့် ကိုက်ညီမှုမရှိပါ။"
+ "ဤ အက်ပ်သည် သင့်တီဗွီနှင့် တွဲဖက်သုံးမရပါ။"
+ "အက်ပ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် သင့်ဖုန်းနှင့် ကိုက်ညီမှုမရှိပါ။"
+ "ပက်ကေ့ဂျ်အဖြစ် ထည့်သွင်းမထားသော အက်ပ်သည် မှန်ကန်မှုမရှိပုံပေါ်သည်။"
+ "%1$s ကို သင့်တက်ဘလက်တွင် ထည့်သွင်းလို့ မရနိုင်ပါ"
+ "သင့်တီဗွီတွင် %1$s အား မတပ်ဆင်နိုင်ပါ။"
+ "%1$s ကို သင့်ဖုန်းတွင် ထည့်သွင်းလို့ မရနိုင်ပါ"
+ "ဖွင့်သည်"
+ "သင်၏ စီမံခန့်ခွဲသူက ရင်းမြစ်မသိသော အက်ပ်များကို ထည့်သွင်းခွင့်မပြုပါ"
+ "အရင်းအမြစ်မသိသော အက်ပ်များကို ဤအသုံးပြုသူက ထည့်သွင်းခွင့်မရှိပါ"
+ "ဤအသုံးပြုသူသည် အက်ပ်များကို ထည့်သွင်းခွင့်မရှိပါ"
+ "OK"
+ "အပလီကေးရှင်းများအား ထိန်းသိမ်းခြင်း"
+ "နေရာလွတ်မရှိပါ"
+ "%1$s ကို ထည့်သွင်းလို့ မရနိုင်ပါ။ နေရာအပိုရအောင် ရှင်းလင်းပြီး ပြန်ကြိုးစားပါ"
+ "အက်ပ်အားမတွေ့ပါ"
+ "ထည့်သွင်းထားသော အပလီကေးရှင်းထဲတွင် ဤအပလီကေးရှင်း မတွေ့ရှိပါ"
+ "ခွင့်ပြုမထားပါ"
+ "ဤဖယ်ရှားမှုပြုလုပ်ရန် လက်ရှိအသုံးပြုသူအား ခွင့်ပြုမထားပါ။"
+ "အမှားအယွင်း"
+ "အက်ပ်ကို ဖယ်ရှား၍မရနိုင်ပါ။"
+ "အပလီကေးရှင်းကို ဖယ်ရှားပါ"
+ "အပ်ဒိတ်လုပ်ထားခြင်းကို ပြန်ထုတ်ပါ"
+ "%1$s ကတော့ အောက်ပါ အက်ပ်၏အစိတ်အပိုင်း တစ်ခု ဖြစ်ပါသည်:"
+ "ဤအပလီကေးရှင်းကို သင်ဖယ်ရှားချင်ပါသလား"
+ "ဤအပလီကေးရှင်းကို အသုံးပြုသူ"" အားလုံး"" အတွက် ဖယ်ရှားချင်ပါသလား? ဤအပလီကေးရှင်း နှင့် သက်ဆိုင်ရာ အချက်အလက်များ အားလုံးကို "" မှ အားလုံးသော "" စက်အသုံးပြုသူတွေအတွက် ဖယ်ရှားပစ်ပါလိမ့်မည်"
+ "သင်သည် အသုံးပြုသူ %1$s အတွက် ဒီအကောင့်ကို ဖြုတ်ပစ်လိုပါသလား?"
+ "ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးမလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။"
+ "ဤအက်ပ်ကို စက်ရုံထုတ်ဗားရှင်းဖြင့် အစားထိုးမလား။ ဒေတာများအားလုံးကို ဖယ်ရှားလိုက်ပါမည်။ ၎င်းသည် အလုပ်ပရိုဖိုင်ဖြင့်သုံးသူများအပါအဝင် အသုံးပြုသူများအားလုံးကို အကျိုးသက်ရောက်စေပါလိမ့်မည်။"
+ "ပရိုဂရမ်ကို ဖယ်ရှားနေပါသည်"
+ "ပရိုဂရမ်ကို ဖယ်ရှားခြင်းမအောင်မြင်ပါ"
+ "ဖယ်ထုတ်သည်"
+ "%1$s ကို ဖယ်ရှားနေပါသည်…"
+ "ဖယ်ရှားခြင်း ပြီးပါပြီ"
+ "%1$s ကို ဖယ်ရှားလိုက်ပါပြီ"
+ "ဖယ်ရှားမှု မအောင်မြင်ပါ"
+ "%1$s ကို ဖယ်ရှားခြင်း မအောင်မြင်ပါ။"
+ "ဖွင့်ထားသော စက်ပစ္စည်းကို စီမံခန့်ခွဲရန်အက်ပ်အား ဖယ်ရှား၍မရပါ"
+ "%1$s အတွက် ဖွင့်ထားသော စက်ပစ္စည်းကို စီမံခန့်ခွဲရန် အက်ပ်အား ဖယ်ရှား၍မရပါ။"
+ "အချို့အသုံးပြုသူများ သို့မဟုတ် ပရိုဖိုင်များအတွက် ဤအက်ပ်ကို လိုအပ်သော်လည်း အချို့သူများအတွက် ဖြုတ်ထားပါသည်"
+ "သင့်ပရိုဖိုင်အတွက် ဤအက်ပ်ကိုလိုအပ်ပြီး ဖြုတ်၍မရပါ။"
+ "ဒီအက်ပ်မှာ သင်၏ ကိရိယာ စီမံအုပ်ချုပ်သူက လိုအပ်သောကြောင့် ဖြုတ်၍ မရနိုင်ပါ။"
+ "စက်ပစ္စည်းကို စီမံခန့်ခွဲရန် အက်ပ်များအား စီမံရန်"
+ "အသုံးပြုသူများအား စီမံခန့်ခွဲပါ"
+ "%1$s ကို ဖယ်ရှားလို့ မရပါ"
+ "ဒေတာအချက်အလက်အစုအားဖတ်ရှုစဉ် ပြသနာ တစ်ခု ဖြစ်ပေါ်ပါသည်"
+ "အသစ်"
+ "အားလုံး"
+ "လုံခြုံမှု"
+ "စက်ပစ္စည်း အသုံးပြုခွင့်"
+ "ဤအပ်ဒိတ်အတွက် ခွင့်ပြုချက်အသစ် မလိုအပ်ပါ"
+ "ငြင်းပယ်သည်"
+ "အခြားအချက်အလက်များ"
+ "မည်သို့ပင်ဖြစ်စေ ငြင်းပယ်ပါ"
+ "%1$s / %2$s"
+ "<b>%1$s</b> ကို %2$s ရန်ခွင့်ပြုမလား။"
+ "<b>%1$s</b> ကို %2$s ရန် အမြဲခွင့်ပြုသလား။"
+ "အက်ပ်အသုံးပြုစဉ်သာ"
+ "အမြဲတမ်း"
+ "ငြင်းဆိုသည်၊ ထပ်မမေးပါနှင့်"
+ "%1$d ခု ပိတ်ထားသည်"
+ "အားလုံးပိတ်ထားသည်"
+ "တစ်ခုမျှ ပိတ်မထားပါ"
+ "ခွင့်ပြုသည်"
+ "အက်ပ်များ"
+ "အက်ပ်ခွင့်ပြုချက်များ"
+ "နောက်ထပ်မမေးပါနှင့်"
+ "ခွင့်ပြုချက်မရှိ"
+ "ထပ်တိုး ခွင့်ပြုချက်များ"
+ "အက်ပ်အချက်အလက် ဖွင့်ရန်"
+
+ နောက်ထပ် %1$d
+ နောက်ထပ် %1$d
+
+ "ဤအက်ပ် အား Android ၏ ဗားရှင်းဟောင်းအတွက် ပုံဆွဲရေးဆွဲထား၏။ ခွင့်ပြုချက်ပေးရန် ငြင်းဆိုပါက ရည်ရွယ်ထားသကဲ့သို့ ဆောင်ရွက်လိမ့်မည် မဟုတ်ပါ။"
+ "အမျိုးအမည်မသိ ဆောင်ရွက်ချက်တစ်ခု လုပ်ရန်"
+ "အက်ပ် %2$d မှ %1$d ခု ခွင့်ပြုသည်"
+ "စနစ်ကိုပြသရန်"
+ "စနစ်ကို ဖျောက်မည်"
+ "အက်ပ် မရှိပါ"
+ "တည်နေရာ ဆက်တင်များ"
+ "%1$s သည် ဤစက်ပစ္စည်းအတွက် တည်နေရာ ဝန်ဆောင်မှုများ ထုတ်ပေးသူဖြစ်သည်။ တည်နေရာ အသုံးပြုမှုကို တည်နေရာချိန်ညှိမှုများတွင် ပြုပြင်နိုင်သည်။"
+ "ဤခွင့်ပြုချက်အား သင် ငြင်းဆိုပါက၊ သင့်စက်ကိရိယာ၏ အခြေခံလုပ်ဆောင်ချက်များသည် ရည်ရွယ်ထားသကဲ့သို့ အလုပ်လုပ်မည် မဟုတ်ပါ။"
+ "မူဝါဒအားဖြင့်ပြဌာန်းရန်"
+ "နောက်ခံတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ပိတ်ထားသည်"
+ "နောက်ခံတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ဖွင့်ထားသည်"
+ "မျက်နှာစာတွင်ဝင်သုံးခွင့်ကို မူဝါဒက ဖွင့်ထားသည်"
+ "စီမံခန့်ခွဲသူက ထိန်းချုပ်ထားသည်"
+
+
+
+ "အမြဲတမ်း"
+ "အက်ပ်အသုံးပြုစဉ်သာ"
+ "ဘယ်တော့မှ"
+ "တင်နေ…"
+ "ခွင့်ပြုချက်များ အားလုံး"
+ "အခြားအပ်ဖ်၏ စွမ်းရည်များ"
+ "ခွင့်ပြုချက် တောင်းခံမှု"
+ "မျက်နှာပြင် ထပ်ပေးမှုကို ရှာတွေ့ခဲ့"
+ "ဒီခွင့်ပြုချက် ဆက်တင်ကို ပြောင်းရန်၊ သင်ဟာ ဦးစွာ ဆက်တင်များ > အက်ပ်များ ထဲတွင် မျက်နှာပြင် ထပ်ပေးမှုကို ပိတ်လိုက်ရန် လိုမယ်"
+ "ဆက်တင်းများ ဖွင့်ရန်"
+ "Android Wear"
+ "Wear ပေါ်တွင် ထည့်သွင်းခြင်း/ဖြုတ်ခြင်းများကို ပံ့ပိုးမထားပါ။"
+ "&It;b7gt;%1$s&It;/b> က အသုံးပြုခွင့်ရမည့် အရာတို့ကို ရွေးပါ"
+ "&It;b>%1$s&It;/b> ကို အပ်ဒိတ်လုပ်ပြီးပါပြီ။ ဤအက်ပ်က အသုံးပြုခွင့်ရမည့်အရာတို့ကို ရွေးပါ။"
+ "မလုပ်တော့"
+ "ဆက်လုပ်ရန်"
+ "ခွင့်ပြုချက် အသစ်များ"
+ "လက်ရှိ ခွင့်ပြုချက်များ"
+ "အက်ပ်ကို ပြင်ဆင်နေသည်…"
+ "အမျိုးအမည်မသိ"
+ "လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏တက်ဘလက်တွင် ထည့်သွင်းခွင့်မရှိပါ။"
+ "လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏တီဗီတွင် ထည့်သွင်းခွင့်မရှိပါ။"
+ "လုံခြုံရေးအရ ဤနေရာမှရယူထားသည့် အမျိုးအမည်မသိသောအက်ပ်များကို သင်၏ဖုန်းတွင် ထည့်သွင်းခွင့်မရှိပါ။"
+ "သင်၏ဖုန်းနှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော ဖုန်းပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"
+ "သင်၏ တက်ဘလက်နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော တက်ဘလက်ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"
+ "သင်၏ TV နှင့် ကိုယ်ရေးကိုယ်တာ အချက်အလက်များသည် အမျိုးအမည် မသိသောအက်ပ်များ၏ တိုက်ခိုက်ခြင်းကို ပိုမိုခံရနိုင်ပါသည်။ ဤအက်ပ်ကို ထည့်သွင်းအသုံးပြုခြင်းအားဖြင့် ဖြစ်ပေါ်လာနိုင်သော TV ပျက်စီးမှု သို့မဟုတ် ဒေတာဆုံးရှုံးမှုများအတွက် သင့်ထံ၌သာ တာဝန်ရှိကြောင်း သဘောတူရာရောက်ပါသည်။"
+ "ဆက်လုပ်ရန်"
+ "ဆက်တင်များ"
+ "wear အက်ပ်ကိုထည့်သွင်းခြင်း/ဖယ်ရှားခြင်း"
+
diff --git a/packages/PackageInstaller/res/values-nb-television/strings.xml b/packages/PackageInstaller/res/values-nb-television/strings.xml
new file mode 100644
index 0000000000000..57f6a21c85b3f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Avvis, og ikke spør igjen"
+ "Du kan endre dette senere i Innstillinger > Apper"
+ "%1$s/%2$s"
+ "Vis systemapper"
+ "Apptillatelser"
+ "Apptillatelser"
+ "Tillatelser for %1$s"
+ "Flere tillatelser"
+ "Tillatelser for %1$s"
+
diff --git a/packages/PackageInstaller/res/values-nb-watch/strings.xml b/packages/PackageInstaller/res/values-nb-watch/strings.xml
new file mode 100644
index 0000000000000..332a8fe38c8d6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Avvis, ikke spør igjen"
+ "%1$s/%2$s"
+ "Vis systemapper"
+ "Kan ikke endres"
+ "Ja"
+ "Avbryt"
+
diff --git a/packages/PackageInstaller/res/values-nb/strings.xml b/packages/PackageInstaller/res/values-nb/strings.xml
new file mode 100644
index 0000000000000..3ff3de46df816
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nb/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pakkeinstallasjon"
+ "Neste"
+ "Installer"
+ "Ferdig"
+ "Avbryt"
+ "Installerer…"
+ "Installerer %1$s …"
+ "Appen er installert."
+ "Ønsker du å installere denne appen? Den får tilgang til følgende:"
+ "Ønsker du å installere denne appen? Den krever ingen spesiell tilgang."
+ "Ønsker du å installere en oppdatering for denne eksisterende appen? Du mister ingen eksisterende data. Den oppdaterte appen får tilgangen spesifisert nedenfor."
+ "Ønsker du å installere en oppdatering for denne innebygde appen? Du mister ingen eksisterende data. Den oppdaterte appen får tilgangen spesifisert nedenfor."
+ "Vil du installere en oppdatering av denne eksisterende appen? De eksisterende dataene dine går ikke tapt. Dette krever ingen spesiell tilgang."
+ "Vil du installere en oppdatering av denne innebygde appen? De eksisterende dataene dine går ikke tapt. Dette krever ingen spesiell tilgang."
+ "Appen ble ikke installert."
+ "Pakken er blokkert fra å bli installert."
+ "Appen ble ikke installert fordi pakken er i konflikt med en eksisterende pakke."
+ "Appen ble ikke installert fordi appen ikke er kompatibel med nettbrettet ditt."
+ "Denne appen er ikke kompatibel med TV-en din."
+ "Appen ble ikke installert fordi appen ikke er kompatibel med telefonen din."
+ "Appen ble ikke installert fordi pakken ser ut til å være ugyldig."
+ "%1$s kunne ikke installeres på nettbrettet ditt."
+ "%1$s kunne ikke installeres på TV-en."
+ "%1$s kunne ikke installeres på telefonen din."
+ "Åpne"
+ "Administratoren din tillater ikke installering av apper som er hentet fra ukjente kilder"
+ "Ukjente apper kan ikke installeres av denne brukeren"
+ "Brukeren har ikke tillatelse til å installere apper"
+ "OK"
+ "Administrer apper"
+ "Tom for plass"
+ "%1$s kunne ikke installeres. Frigjør plass og prøv på nytt."
+ "Appen ble ikke funnet"
+ "Finner ikke appen i listen over installerte apper."
+ "Ikke tillatt"
+ "Denne brukeren har ikke tillatelse til å utføre denne avinstalleringen."
+ "Feil"
+ "Kunne ikke avinstallere appen."
+ "Avinstaller appen"
+ "Avinstaller oppdateringen"
+ "%1$s er del av følgende app:"
+ "Vil du avinstallere denne appen?"
+ "Vil du avinstallere denne appen for ""alle"" brukere? Appen og tilhørende data blir fjernet fra ""alle"" brukere på enheten."
+ "Ønsker du å avinstallere denne appen for brukeren %1$s?"
+ "Vil du erstatte denne appen med den opprinnelige versjonen? Alle dataene fjernes."
+ "Vil du erstatte denne appen med den opprinnelige versjonen? Alle dataene fjernes. Dette påvirker alle som bruker denne enheten – også personer med jobbprofiler."
+ "Avinstalleringer som er i gang"
+ "Mislykkede avinstalleringer"
+ "Avinstallerer…"
+ "Avinstallerer %1$s …"
+ "Avinstalleringen er fullført."
+ "Avinstallerte %1$s"
+ "Avinstalleringen mislyktes."
+ "Kunne ikke avinstallere %1$s."
+ "Kan ikke avinstallere den aktive appen for enhetsadministrator"
+ "Kan ikke avinstallere den aktive appen for enhetsadministrator for %1$s"
+ "Appen er nødvendig for noen brukere eller profiler, og den er avinstallert for andre"
+ "Denne appen er nødvendig for profilen din og kan ikke avinstalleres."
+ "Denne appen kreves av enhetsadministratoren din og kan ikke avinstalleres."
+ "Administrer apper for enhetsadministrator"
+ "Administrer brukere"
+ "%1$s kunne ikke installeres."
+ "Det oppsto et problem med analysen av pakken."
+ "Nye"
+ "Alle"
+ "Personvern"
+ "Enhetstilgang"
+ "Denne oppdateringen krever ingen nye tillatelser."
+ "Ikke tillat"
+ "Finn ut mer"
+ "Avvis likevel"
+ "%1$s av %2$s"
+ "Vil du gi <b>%1$s</b> tillatelse til å %2$s?"
+ "Vil du alltid tillate at <b>%1$s</b> kan %2$s?"
+ "Bare når appen brukes"
+ "Alltid"
+ "Avvis, og ikke spør igjen"
+ "%1$d er slått av"
+ "alt er slått av"
+ "ingen er slått av"
+ "Tillat"
+ "Apper"
+ "Apptillatelser"
+ "Ikke spør igjen"
+ "Ingen tillatelser"
+ "Flere tillatelser"
+ "Åpne info om appen"
+
+ %1$d til
+ %1$d til
+
+ "Denne appen er designet for en eldre versjon av Android. Hvis du nekter å gi tillatelse, kan det føre til at den ikke lenger fungerer etter hensikten."
+ "utfør en ukjent handling"
+ "%1$d av %2$d apper er tillatt"
+ "Vis systemapper"
+ "Skjul systemet"
+ "Ingen apper"
+ "Posisjonsinnstillinger"
+ "%1$s er en leverandør av posisjonstjenester for denne enheten. Tilgang til posisjon kan endres fra posisjonsinnstillingene."
+ "Hvis du ikke gir denne tillatelsen, kan grunnleggende funksjoner på enheten slutte å fungere som de skal."
+ "Påkrevd ifølge retningslinjene"
+ "Bakgrunnstilgang er slått av pga. retningslinjene"
+ "Bakgrunnstilgang er slått på pga. retningslinjene"
+ "Forgrunnstilgang er slått på pga. retningslinjene"
+ "Kontrollert av administratoren"
+
+
+
+ "Alltid"
+ "Bare når appen brukes"
+ "Aldri"
+ "Laster inn …"
+ "Alle tillatelser"
+ "Andre appfunksjoner"
+ "Forespørsel om tillatelse"
+ "Skjermoverlegg oppdaget"
+ "For å endre denne tillatelsesinnstilingen må du først slå av skjermoverlegget fra Innstillinger > Apper"
+ "Åpne innstillingene"
+ "Android Wear"
+ "Handlinger for å installere og avinstallere er ikke støttet på Wear."
+ "Velg hva du vil gi %1$s tilgang til"
+ "%1$s er oppdatert. Velg hva du vil gi denne appen tilgang til."
+ "Avbryt"
+ "Fortsett"
+ "Nye tillatelser"
+ "Gjeldende tillatelser"
+ "Setter opp appen …"
+ "Ukjent"
+ "Nettbrettet ditt har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."
+ "TV-en din har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."
+ "Telefonen din har ikke tillatelse til å installere ukjente apper fra denne kilden, for å ivareta sikkerheten din."
+ "Telefonen din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på telefonen eller tap av data bruk av appen forårsaker."
+ "Nettbrettet ditt og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på nettbrettet eller tap av data bruk av appen forårsaker."
+ "TV-en din og de personlige dataene dine er mer sårbare for angrep fra ukjente apper. Når du installerer denne appen, samtykker du i at du er ansvarlig for eventuelle skader på TV-en eller tap av data bruk av appen forårsaker."
+ "Fortsett"
+ "Innstillinger"
+ "Installerer/avinstallerer wear-apper"
+
diff --git a/packages/PackageInstaller/res/values-ne-television/strings.xml b/packages/PackageInstaller/res/values-ne-television/strings.xml
new file mode 100644
index 0000000000000..d6e908cfd17e3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "अस्वीकृत गर्नुहोस् र फेरि नसोध्नुहोस्"
+ "तपाईं यसलाई पछि सेटिङ > अनुप्रयोगमा बदल्न सक्नु हुन्छ"
+ "%1$s / %2$s"
+ "प्रणाली अनुप्रयोगहरू देखाउनुहोस्"
+ "अनुप्रयोग सम्बन्धी अनुमतिहरू"
+ "अनुप्रयोग सम्बन्धी अनुमतिहरू"
+ "%1$s सम्बन्धी अनुमतिहरू"
+ "अतिरिक्त अनुमतिहरू"
+ "%1$s सम्बन्धी अनुमतिहरू"
+
diff --git a/packages/PackageInstaller/res/values-ne-watch/strings.xml b/packages/PackageInstaller/res/values-ne-watch/strings.xml
new file mode 100644
index 0000000000000..ffcc2f7f2a8e3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "अस्वीकार गर्नुहोस्, फेरि नसोध्नुहोस्"
+ "%1$s / %2$s"
+ "प्रणाली अनुप्रयोगहरू देखाउनुहोस्"
+ "परिवर्तन गर्न सकिँदैन"
+ "हो"
+ "रद्द गर्नुहोस्"
+
diff --git a/packages/PackageInstaller/res/values-ne/strings.xml b/packages/PackageInstaller/res/values-ne/strings.xml
new file mode 100644
index 0000000000000..b2dea8f2349aa
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ne/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "प्याकेज स्थापनकर्ता"
+ "अर्को"
+ "स्थापना गर्नुहोस्"
+ "भयो"
+ "रद्द गर्नुहोस्"
+ "स्थापित हुँदै…"
+ "%1$s स्थापना गर्दै…"
+ "अनुप्रयोग स्थापना भयो।"
+ "के तपाईं यो अनुप्रयोग स्थापन गर्न चाहनु हुन्छ? यसले पहुँच प्राप्त गर्ने छ:"
+ "के तपाईं यस अनुप्रयोगलाई जडान गर्न चाहनु हुन्छ? यसको लागि कुनै विशेष पहुँचको आवश्यकता पर्दैन।"
+ "के तपाईँसँग अहिले भईरहेको अनुप्रयोगको एक अपडेट लाई स्थापित गर्न चाहानुहुन्छ? तपाईँको अहिलेको डेटा हराउने छैन। अपडेट भएको अनुप्रयोग पहुँच पाउनेछ मा:"
+ "के तपाईं यस पूर्व-निर्मित अनुप्रयोगमा अपडेट स्थापित गर्न चाहनु हुन्छ? तपाईंको रहेको डेटा हराउने छैन। अपडेट गरिएको अनुप्रयोगले पहुँच पाउने छ:"
+ "के तपाईँसँग अहिले भइरहेको अनुप्रयोगको एउटा अपडेटलाई स्थापित गर्न चाहनु हुन्छ? तपाईँको अहिलेको डेटा हराउने छैन। यसलाई कुनै विशेष पहुँचको आवश्यकता छैन।"
+ "के तपाईं यस जोडिएको अनुप्रयोगको एउटा अपडेटलाई स्थापित गर्न चाहनुहुन्छ? तपाईंको अहिलेको डेटा हराउने छैन। यसलाई कुनै विशेष पहुँचको आवश्यकता छैन।"
+ "अनुप्रयोग स्थापना भएन।"
+ "यो प्याकेज स्थापना हुनबाट अवरुद्ध भएको थियो।"
+ "प्याकेजका रूपमा स्थापना नगरिएको अनुप्रयोग विद्यमान प्याकेजसँग मेल खाँदैन।"
+ "अनुप्रयोगका रूपमा स्थापना नगरिएको अनुप्रयोग तपाईंको ट्याब्लेटसँग मिल्दो छैन।"
+ "यो अनुप्रयोग तपाईँको TV को लागि उपयुक्त छैन।"
+ "अनुप्रयोगका रूपमा स्थापना नगरिएको अनुप्रयोग तपाईंको फोनसँग मिल्दो छैन।"
+ "प्याकेजका रूपमा स्थापना नगरिएको अनुप्रयोग अमान्य जस्तो देखिन्छ।"
+ "%1$s तपाईँको ट्याब्लेटमा स्थापित हुन सकेन।"
+ "तपाईँको TVमा%1$sस्थापना गर्न सकिएन।"
+ "तपाईँको फोनमा %1$s जडान हुन सकेन।"
+ "खोल्नुहोस्"
+ "तपाईंका प्रशासकले अज्ञात स्रोतहरूबाट प्राप्त अनुप्रयोगहरूलाई स्थापना गर्ने अनुमति दिनुहुन्न"
+ "यस प्रयोगकर्ताले अज्ञात अनुप्रयोगहरू स्थापना गर्न सक्नुहुन्न"
+ "यो प्रयोगकर्तालाई अनुप्रयोगहरूको स्थापना गर्ने अनुमति छैन"
+ "ठिक छ"
+ "अनुप्रयोगहरूको व्यवस्थापन गर्नुहोस्"
+ "ठाउँभन्दा बाहिर"
+ "%1$s स्थापन गर्न सकिएन। केही ठाउँ खाली गर्नुहोस् र फेरि कोसिस गर्नुहोस्"
+ "अनुप्रयोग फेला परेन"
+ "स्थापना भएको अनुप्रयोगहरू सूचीमा अनुप्रयोग फेला परेको थिएन।"
+ "अनुमति छैन"
+ "हालको प्रयोगकर्तालाई यो स्थापना रद्द गर्ने कार्य गर्ने अनुमति छैन।"
+ "त्रुटि"
+ "अनुप्रयोगको स्थापना रद्द गर्न सकिएन।"
+ "अनुप्रयोग अस्थापना गर्नुहोस्"
+ "परिस्कारहरू अस्थापना गर्नुहोस्"
+ "%1$s निम्न अनुप्रयोगको अंश हो:"
+ "के तपाईं यो अनुप्रयोग अस्थापना गर्न चाहनु हुन्छ?"
+ "के तपाईं यो अनुप्रयोग ""सबै"" प्रयोगकर्ताहरूको लागि स्थापना रद्द गर्न चाहनु हुन्छ? अनुप्रयोग र यसको डेटा ""सबै"" प्रयोगकर्ताहरूबाट उपकरणमा हटाइने छ।"
+ "के तपाईं प्रयोगकर्ता %1$s को लागि यो अनुप्रयोग स्थापना रद्द गर्न चाहनुहुन्छ?"
+ "यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइनेछ।"
+ "यस अनुप्रयोगलाई फ्याक्ट्रीको संस्करणले बदल्ने हो? सबै डेटा हटाइनेछ। यसले यस यन्त्रका कार्य प्रोफाइल भएका लगायत सबै प्रयोगकर्ताहरूमा असर पार्छ।"
+ "चलिरहेका स्थापना रद्द गर्ने कार्यहरू"
+ "असफल भएका स्थापना रद्द गर्ने कार्यहरू"
+ "अस्थापना गर्दै..."
+ "%1$s को स्थापना रद्द गर्दै…"
+ "स्थापना रद्द गर्न सकियो।"
+ "%1$s को स्थापना रद्द गरियो"
+ "अस्थापना गर्न असफल"
+ "%1$s को स्थापना रद्द गर्ने कार्य असफल भयो।"
+ "यन्त्रको सक्रिय प्रशासकीय अनुप्रयोगको स्थापना रद्द गर्न मिल्दैन"
+ "%1$s को यन्त्रको सक्रिय प्रशासकीय अनुप्रयोगको स्थापना रद्द गर्न मिल्दैन"
+ "अन्य प्रयोगकर्ताहरूका लागि यस अनु्प्रयोगको स्थापना रद्द गरे पनि केही प्रयोगकर्ता वा प्रोफाइलहरूलाई यसको आवश्यकता पर्दछ"
+ "यो अनुप्रयोग तपाईँको प्रोफाइलका लागि आवश्यक छ र यसको स्थापनालाई रद्द गर्न सकिँदैन।"
+ "यो अनुप्रयोग तपाईँको उपकरण प्रशासकलाई आवश्यक छ र स्थापना रद्द गर्न सकिँदैन।"
+ "यन्त्रका प्रशासकीय अनुप्रयोगहरूको व्यवस्थापन गर्नुहोस्"
+ "प्रयोगकर्ताहरूलाई व्यवस्थापन गर्नुहोस्"
+ "%1$s स्थापना रद्द गर्न सकिँदैन।"
+ "प्याकेजलाई पार्सिङ गर्दा एउटा समस्या आयो।"
+ "नयाँ"
+ "सबै"
+ "गोपनीयता"
+ "उपकरण पहुँच"
+ "यस अपडेटलाई नयाँ अनुमति आवश्यक पर्दैन।"
+ "अस्वीकार गर्नुहोस्"
+ "थप जानकारी"
+ "जे भए पनि अस्वीकार गर्नुहोस्"
+ "%2$s को %1$s"
+ "<b>%1$s</b> लाई %2$s गर्न अनुमति दिने हो?"
+ "<b>%1$s</b> लाई सधैँ %2$s गर्ने अनुमति दिने हो?"
+ "अनुप्रयोग प्रयोग गर्दा मात्र"
+ "सधैँ"
+ "अस्वीकार गरी फेरि नसोध्नुहोस्"
+ "%1$d अनुमतिहरूलाई असक्षम पारिएको छ"
+ "सबै अनुमतिहरूलाई असक्षम पारिएको छ"
+ "कुनै पनि अनुमतिलाई असक्षम पारिएको छैन"
+ "अनुमति दिनुहोस्"
+ "अनुप्रयोगहरू"
+ "अनुप्रयोग अनुमतिहरू"
+ "फेरि नसोध्नुहोस्"
+ "कुनै अनुमतिहरू छैनन्"
+ "अतिरिक्त अनुमतिहरू"
+ "अनुप्रयोगसम्बन्धी जानकारी खोल्नुहोस्"
+
+ %1$d थप
+ %1$d थप
+
+ "यो अनुप्रयोग Android को पुरानो संस्करणका लागि डिजाइन गरिएको थियो। अनुमति अस्वीकृत गर्नाले यसले चाहिएको जस्तो कार्य नगर्न सक्छ।"
+ "एउटा अज्ञात कार्य गर्नुहोस्"
+ "%2$d को %1$d अनुप्रयोगहरूलाई अनुमति छ"
+ "प्रणाली देखाउनुहोस्"
+ "प्रणाली लुकाउनुहोस्"
+ "कुनै अनुप्रयोगहरू छैनन्।"
+ "स्थान सेटिङहरू"
+ "%1$s यो यन्त्रका लागि स्थान सेवाहरूको एउटा प्रदायक हो। स्थान पहुँच स्थान सेटिङहरूबाट परिमार्जन गर्न सकिन्छ।"
+ "तपाईँले यो अनुमति अस्वीकार गर्नुभयो भने तपाईँको यन्त्रका मूल विशेषताहरू अब चाहेअनुसार कार्य नगर्न सक्छ।"
+ "नीतिद्वारा लागू गरियो"
+ "नीतिले पृष्ठभूमिको पहुँचलाई असक्षम पारेको छ"
+ "नीतिले पृष्ठभूमिको पहुँचलाई सक्षम पारेको छ"
+ "नीतिले अग्रभूमिको पहुँचलाई सक्षम पारेको छ"
+ "प्रशासकले नियन्त्रण गर्नुभएको"
+
+
+
+ "सधैँ"
+ "अनुप्रयोग प्रयोग गर्दा"
+ "कहिल्यै होइन"
+ "लोड हुँदैछ..."
+ "सबै अनुमतिहरू"
+ "अन्य अनुप्रयोग क्षमताहरू"
+ "अनुमति अनुरोध"
+ "स्क्रिन ओभरले पत्ता लाग्यो"
+ "यो अनुमति सेटिङ परिवर्तन गर्न, तपाईँले पहिला सेटिङ अनुप्रयोगबाट स्क्रिन ओभरले बन्द गर्नु पर्दछ।"
+ "सेटिङहरू खोल्नुहोस्"
+ "Android Wear"
+ "Wear मा स्थापना/स्थापना रद्द गर्ने कारबाहीहरू समर्थित छैनन्।"
+ "<b>%1$s</b> लाई के माथि पहुँच राख्न दिने भन्ने कुरा छनौट गर्नुहोस्"
+ "<b>%1$s</b> लाई अद्यावधिक गरिएको छ। यस अनुप्रयोगलाई के माथि पहुँच राख्न दिने भन्ने कुरा छनौट गर्नुहोस्।"
+ "रद्द गर्नुहोस्"
+ "जारी राख्नुहोस्"
+ "नयाँ अनुमतिहरू"
+ "वर्तमान अनुमतिहरू"
+ "अनुप्रयोगलाई तयार पार्दै…"
+ "अज्ञात"
+ "तपाईंको सुरक्षाको लागि, तपाईंको ट्याब्लेटलाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"
+ "तपाईंको सुरक्षाको लागि, तपाईंको TV लाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"
+ "तपाईंको सुरक्षाको लागि, तपाईंको फोनलाई यो स्रोतबाट प्राप्त हुने अज्ञात अनुप्रयोगहरू स्थापना गर्ने अनुमति छैन।"
+ "तपाईंको फोन र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको फोनलाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"
+ "तपाईंको ट्याब्लेट र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको ट्याब्लेटलाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"
+ "तपाईंको TV र व्यक्तिगत डेटा अज्ञात अनुप्रयोगहरूबाट हुने आक्रमणको चपेटामा पर्ने बढी जोखिममा हुन्छन्। यो अनुप्रयोग स्थापना गरेर तपाईं यसको प्रयोगबाट तपाईंको TV लाई हुनसक्ने क्षति वा डेटाको नोक्सानीका लागि स्वयं जिम्मेवार हुनुहुन्छ भन्ने कुरामा सहमत हुनुहुन्छ।"
+ "जारी राख्नुहोस्"
+ "सेटिङहरू"
+ "वेयर एपहरूको स्थापना/स्थापना रद्द गर्दै"
+
diff --git a/packages/PackageInstaller/res/values-nl-television/strings.xml b/packages/PackageInstaller/res/values-nl-television/strings.xml
new file mode 100644
index 0000000000000..c7256e80ead90
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Weigeren en niet meer vragen"
+ "U kunt dit later wijzigen in Instellingen > Apps"
+ "%1$s/%2$s"
+ "Systeem weergeven"
+ "App-machtigingen"
+ "App-machtigingen"
+ "Machtigingen voor %1$s"
+ "Aanvullende machtigingen"
+ "Machtigingen voor %1$s"
+
diff --git a/packages/PackageInstaller/res/values-nl-watch/strings.xml b/packages/PackageInstaller/res/values-nl-watch/strings.xml
new file mode 100644
index 0000000000000..338b79b98819e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Weigeren, niet meer vragen"
+ "%1$s/%2$s"
+ "Systeem weergeven"
+ "Niet aanpasbaar"
+ "Ja"
+ "Annuleren"
+
diff --git a/packages/PackageInstaller/res/values-nl/strings.xml b/packages/PackageInstaller/res/values-nl/strings.xml
new file mode 100644
index 0000000000000..a9e89400708e9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-nl/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Pakket-installatie"
+ "Volgende"
+ "Installeren"
+ "Gereed"
+ "Annuleren"
+ "Installeren..."
+ "%1$s installeren…"
+ "App geïnstalleerd."
+ "Wil je deze app installeren? Deze krijgt toegang tot:"
+ "Wil je deze app installeren? Hiervoor is geen speciale toegang vereist."
+ "Wil je een update voor deze bestaande app installeren? Je huidige gegevens gaan niet verloren. De bijgewerkte app krijgt toegang tot:"
+ "Wil je een update van deze ingebouwde app installeren? Je huidige gegevens gaan niet verloren. De bijgewerkte app krijgt toegang tot:"
+ "Wil je een update voor deze bestaande app installeren? Je huidige gegevens gaan niet verloren. Hiervoor is geen speciale toegang vereist."
+ "Wil je een update voor deze ingebouwde app installeren? Je huidige gegevens gaan niet verloren. Hiervoor is geen speciale toegang vereist."
+ "App niet geïnstalleerd."
+ "De installatie van het pakket is geblokkeerd."
+ "App die niet is geïnstalleerd als pakket conflicteert met een bestaand pakket."
+ "App die niet is geïnstalleerd als app is niet geschikt voor je tablet."
+ "Deze app is niet compatibel met je tv."
+ "App die niet is geïnstalleerd als app is niet geschikt voor je telefoon."
+ "App die niet is geïnstalleerd als pakket lijkt ongeldig te zijn."
+ "%1$s kan niet worden geïnstalleerd op je tablet."
+ "%1$s kan niet worden geïnstalleerd op je tv."
+ "%1$s kan niet worden geïnstalleerd op je telefoon."
+ "Openen"
+ "Je beheerder staat de installatie van apps afkomstig van onbekende bronnen niet toe"
+ "Onbekende apps kunnen niet worden geïnstalleerd door deze gebruiker"
+ "Deze gebruiker mag geen apps installeren"
+ "OK"
+ "Apps beheren"
+ "Geen ruimte beschikbaar"
+ "%1$s kan niet worden geïnstalleerd. Maak ruimte vrij en probeer het opnieuw."
+ "App niet gevonden"
+ "De app is niet gevonden in de lijst met geïnstalleerde apps."
+ "Niet toegestaan"
+ "De huidige gebruiker mag deze verwijdering niet uitvoeren."
+ "Fout"
+ "App kan niet worden verwijderd."
+ "App verwijderen"
+ "Update verwijderen"
+ "%1$s maakt deel uit van de volgende app:"
+ "Wil je deze app verwijderen?"
+ "Wil je deze app verwijderen voor ""alle"" gebruikers? Deze app en de gegevens ervan worden verwijderd voor ""alle"" gebruikers van het apparaat."
+ "Wil je deze app verwijderen voor de gebruiker %1$s?"
+ "Deze app vervangen door de fabrieksversie? Alle gegevens worden verwijderd."
+ "Deze app vervangen door de fabrieksversie? Alle gegevens worden verwijderd. Dit geldt voor alle gebruikers van het apparaat, dus ook voor gebruikers met een werkprofiel."
+ "Actieve verwijderingen"
+ "Mislukte verwijderingen"
+ "Verwijderen..."
+ "%1$s verwijderen…"
+ "Verwijdering voltooid."
+ "%1$s verwijderd"
+ "Verwijdering mislukt."
+ "%1$s kan niet worden verwijderd."
+ "Kan actieve apparaatbeheer-app niet verwijderen"
+ "Kan actieve apparaatbeheer-app niet verwijderen voor %1$s"
+ "Deze app is vereist voor sommige gebruikers of profielen en is verwijderd voor andere"
+ "Deze app is vereist voor je profiel en kan niet worden verwijderd."
+ "Deze app is vereist door je apparaatbeheerder en kan niet worden verwijderd."
+ "Apparaatbeheer-apps beheren"
+ "Gebruikers beheren"
+ "%1$s kan niet worden verwijderd."
+ "Er is een probleem opgetreden bij het parseren van het pakket."
+ "Nieuw"
+ "Alle"
+ "Privacy"
+ "Apparaattoegang"
+ "Voor deze update zijn geen nieuwe machtigingen vereist."
+ "Weigeren"
+ "Meer informatie"
+ "Toch weigeren"
+ "%1$s van %2$s"
+ "<b>%1$s</b> het volgende toestaan: %2$s."
+ "<b>%1$s</b> altijd toestaan om %2$s?"
+ "Alleen als app in gebruik is"
+ "Altijd"
+ "Weigeren en niet meer vragen"
+ "%1$d uitgeschakeld"
+ "alle rechten uitgeschakeld"
+ "geen rechten uitgeschakeld"
+ "Toestaan"
+ "Apps"
+ "App-machtigingen"
+ "Niet meer vragen"
+ "Geen machtigingen"
+ "Aanvullende machtigingen"
+ "App-info openen"
+
+ Nog %1$d
+ Nog %1$d
+
+ "Deze app is ontworpen voor een oudere versie van Android. Als u geen toestemming geeft, kan de app mogelijk niet functioneren zoals is bedoeld."
+ "een onbekende actie uitvoeren"
+ "Verleend aan %1$d van %2$d apps"
+ "Systeem weergeven"
+ "Systeem-apps verbergen"
+ "Geen apps"
+ "Locatie-instellingen"
+ "%1$s is een leverancier van locatieservices voor dit apparaat. Locatietoegang kan worden aangepast via de locatie-instellingen."
+ "Als je deze machtiging weigert, kan het zijn dat basisfuncties van je apparaat niet meer werken zoals bedoeld."
+ "Afgedwongen door beleid"
+ "Toegang op de achtergrond uitgeschakeld op basis van beleid"
+ "Toegang op de achtergrond ingeschakeld op basis van beleid"
+ "Toegang op de voorgrond ingeschakeld op basis van beleid"
+ "Ingesteld door beheerder"
+
+
+
+ "Altijd"
+ "Alleen als app in gebruik is"
+ "Nooit"
+ "Laden…"
+ "Alle machtigingen"
+ "Andere app-mogelijkheden"
+ "Toestemmingsverzoek"
+ "Schermoverlay gedetecteerd"
+ "Als u deze instelling voor rechten wilt wijzigen, moet u eerst de schermoverlay uitschakelen via \'Instellingen\' > \'Apps\'"
+ "Instellingen openen"
+ "Android Wear"
+ "Acties voor installeren/verwijderen niet ondersteund op Wear."
+ "Kiezen waartoe <b>%1$s</b> toegang krijgt"
+ "<b>%1$s</b> is geüpdatet. Kies waartoe je deze app toegang wilt geven."
+ "Annuleren"
+ "Doorgaan"
+ "Nieuwe machtigingen"
+ "Huidige machtigingen"
+ "App uitvoeren…"
+ "Onbekend"
+ "Uit veiligheidsoverwegingen heeft je tablet geen toestemming om onbekende apps van deze bron te installeren."
+ "Uit veiligheidsoverwegingen heeft je tv geen toestemming om onbekende apps van deze bron te installeren."
+ "Uit veiligheidsoverwegingen heeft je telefoon geen toestemming om onbekende apps van deze bron te installeren."
+ "Je telefoon en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je telefoon of gegevensverlies als gevolg van het gebruik van de app."
+ "Je tablet en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tablet of gegevensverlies als gevolg van het gebruik van de app."
+ "Je tv en persoonlijke gegevens zijn kwetsbaarder voor aanvallen door onbekende apps. Als je deze app installeert, ga je ermee akkoord dat je verantwoordelijk bent voor eventuele schade aan je tv of gegevensverlies als gevolg van het gebruik van de app."
+ "Doorgaan"
+ "Instellingen"
+ "Wear-apps installeren/verwijderen"
+
diff --git a/packages/PackageInstaller/res/values-notround-watch/dimens.xml b/packages/PackageInstaller/res/values-notround-watch/dimens.xml
new file mode 100644
index 0000000000000..55d62488962f6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-notround-watch/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ 44dp
+ 8dp
+ 16dp
+ 8dp
+ 8dp
+
diff --git a/packages/PackageInstaller/res/values-or-television/strings.xml b/packages/PackageInstaller/res/values-or-television/strings.xml
new file mode 100644
index 0000000000000..157b9221bdadb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ମନାକରନ୍ତୁ ଏବଂ ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"
+ "ସେଟିଙ୍ଗ ଓ ଆପ୍ରେ ଏହାକୁ ଆପଣ ପରେ ବଦଳାଇପାରିବେ"
+ "%1$s / %2$s"
+ "ସିଷ୍ଟମ୍ ଆପ୍ ଦେଖାନ୍ତୁ"
+ "ଆପ୍ର ଅନୁମତି"
+ "ଆପ୍ର ଅନୁମତି"
+ "%1$s ଅନୁମତି"
+ "ଅତିରିକ୍ତ ଅନୁମତି"
+ "%1$s ଅନୁମତି"
+
diff --git a/packages/PackageInstaller/res/values-or-watch/strings.xml b/packages/PackageInstaller/res/values-or-watch/strings.xml
new file mode 100644
index 0000000000000..b7719a1623751
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ମନାକରନ୍ତୁ, ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"
+ "%1$s / %2$s"
+ "ସିଷ୍ଟମ୍ ଆପ୍ ଦେଖାନ୍ତୁ"
+ "ବଦଳାଯାଇପାରିବ ନାହିଁ"
+ "ହଁ"
+ "କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"
+
diff --git a/packages/PackageInstaller/res/values-or/strings.xml b/packages/PackageInstaller/res/values-or/strings.xml
new file mode 100644
index 0000000000000..8d93c6fa359aa
--- /dev/null
+++ b/packages/PackageInstaller/res/values-or/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ପ୍ୟାକେଜ୍ ଇନଷ୍ଟଲର୍"
+ "ପରବର୍ତ୍ତୀ"
+ "ଇନଷ୍ଟଲ୍ କରନ୍ତୁ"
+ "ହୋଇଗଲା"
+ "କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"
+ "ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"
+ "%1$s ଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"
+ "ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇଗଲା।"
+ "ଏହି ଆପ୍ଲିକେଶନ୍ ଆପଣ ଇନଷ୍ଟଲ୍ କରିବେ କି? ଏହିଗୁଡ଼ିକୁ ଏହା ଆକ୍ସେସ୍ କରିପାରିବ:"
+ "ଏହି ଆପ୍ଲିକେଶନ୍ ଆପଣ ଇନଷ୍ଟଲ୍ କରିବେ କି? ଏହା କୌଣସି ବିଶେଷ ପ୍ରକାରର ଆକ୍ସେସ୍ ଆବଶ୍ୟକ କରେନାହିଁ।"
+ "ପୂର୍ବରୁ ଥିବା ଏହି ଆପ୍ଲିକେଶନ୍ରେ ଆପଣ ଏକ ଅପଡେଟ୍ ଇନଷ୍ଟଲ୍ କରିବେ କି? ଏଥିରେ ଥିବା ଆପଣଙ୍କ ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଅପଡେଟ୍ ହେବାପରେ ଆପ୍ଲିକେଶନ୍ଟି ଏହିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ କରିପାରିବ:"
+ "ଏହି ବିଲ୍ଟ-ଇନ୍ ଆପ୍ଲିକେଶନ୍ରେ ଆପଣ ଏକ ଅପଡେଟ୍ ଇନଷ୍ଟଲ୍ କରିବେ କି? ଏଥିରେ ଥିବା ଆପଣଙ୍କ ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଅପଡେଟ୍ ହେବାପରେ ଆପ୍ଲିକେଶନ୍ଟି ଏହିଗୁଡ଼ିକୁ ଆକ୍ସେସ୍ କରିପାରିବ:"
+ "ଆପଣ ବର୍ତ୍ତମାନର ଏହି ଆପ୍ଲିକେଶନ୍ର ଏକ ଅପଡେଟ୍ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଏଥିରେ କୌଣସି ବିଶେଷ ଆକ୍ସେସ୍ର ଆବଶ୍ୟକତା ନାହିଁ।"
+ "ଆପଣ ବର୍ତ୍ତମାନର ଏହି ବିଲ୍ଟ-ଇନ୍ ଆପ୍ଲିକେଶନ୍ର ଏକ ଅପଡେଟ୍ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଆପଣଙ୍କ ବର୍ତ୍ତମାନର ଡାଟା ନଷ୍ଟ ହେବନାହିଁ। ଏଥିରେ କୌଣସି ବିଶେଷ ଆକ୍ସେସ୍ର ଆବଶ୍ୟକତା ନାହିଁ।"
+ "ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇନାହିଁ।"
+ "ଏହି ପ୍ୟାକେଜ୍କୁ ଇନଷ୍ଟଲ୍ କରାଯିବାରେ ଅବରୋଧ କରାଯାଇଥିଲା।"
+ "ପୂର୍ବରୁ ଥିବା ପ୍ୟାକେଜ୍ ସହ ଏହି ପ୍ୟାକେଜ୍ର ସମସ୍ୟା ଉପୁଯିବାରୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"
+ "ଆପ୍ ଆପଣଙ୍କ ଟାବଲେଟ୍ ସହ କମ୍ପାଟିବଲ୍ ନଥିବା ହେତୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"
+ "ଏହି ଆପ୍, ଆପଣଙ୍କ ଟିଭି ସହ କମ୍ପାଟିବଲ୍ ନୁହେଁ।"
+ "ଆପ୍ ଆପଣଙ୍କ ଫୋନ୍ ସହ କମ୍ପାଟିବଲ୍ ନଥିବା ହେତୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"
+ "ପ୍ୟାକେଜ୍ ଅମାନ୍ୟ ଥିବା ପରି ଜଣାପଡ଼ିବାରୁ ଆପ୍ ଇନଷ୍ଟଲ୍ ହୋଇପାରିଲା ନାହିଁ।"
+ "%1$sଟି ଆପଣଙ୍କ ଟାବଲେଟ୍ରେ ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ।"
+ "%1$s ଆପଣଙ୍କ ଟିଭିରେ ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ।"
+ "%1$sଟି ଆପଣଙ୍କ ଫୋନ୍ରେ ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ।"
+ "ଖୋଲନ୍ତୁ"
+ "ଅଜଣା ସୋର୍ସରୁ ଆସିଥିବା ଆପଗୁଡ଼ିକ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଆପଣଙ୍କ ଆଡମିନ୍ ଅନୁମତି ଦିଅନ୍ତି ନାହିଁ"
+ "ଏହି ୟୁଜରଙ୍କ ଦ୍ୱାରା ଅଜଣା ଆପ୍ ଇନଷ୍ଟଲ୍ କରାଯାଇ ପାରିବ ନାହିଁ"
+ "ଏହି ୟୁଜର୍ ଜଣକ ଆପ୍ ଇନଷ୍ଟଲ୍ କରିପାରିବେ ନାହିଁ"
+ "ଠିକ୍ ଅଛି"
+ "ଆପ୍ଗୁଡ଼ିକର ପରିଚାଳନା କରନ୍ତୁ"
+ "ଆଉ ସ୍ଥାନ ନାହିଁ"
+ "%1$s ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ। କିଛି ସ୍ଥାନ ଖାଲିକରି ପୁଣିଥରେ ଚେଷ୍ଟା କରନ୍ତୁ।"
+ "ଆପ୍ ମିଳିଲା ନାହିଁ"
+ "ଇନଷ୍ଟଲ୍ କରାଯାଇଥିବା ଆପ୍ ତାଲିକାରେ ଆପ୍ଟି ମିଳିଲା ନାହିଁ।"
+ "ଅନୁମତି ଦିଆଯାଇନାହିଁ"
+ "ଏହି ଅନଇନଷ୍ଟଲେଶନ୍ କରିବାକୁ ବର୍ତ୍ତମାନର ୟୁଜରଙ୍କୁ ଅନୁମତି ଦିଆଯାଇନାହିଁ।"
+ "ତ୍ରୁଟି"
+ "ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରାହେଲା ନାହିଁ"
+ "ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"
+ "ଅପଡେଟ୍ ଅନଇନଷ୍ଟଲ୍ କରନ୍ତୁ"
+ "%1$s ହେଉଛି ନିମ୍ନ ଆପ୍ର ଏକ ଅଂଶ।"
+ "ଆପଣ ଏହି ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରିବାକୁ ଚାହାଁନ୍ତି କି?"
+ "ଆପଣ ""ସମସ୍ତ"" ୟୁଜର୍ଙ୍କ ପାଇଁ ଏହି ଆପ୍କୁ ଅନଷ୍ଟଲ୍ କରିବାକୁ ଚାହୁଁଛନ୍ତି କି? ଡିଭାଇସ୍ରେ ଥିବା ""ସମସ୍ତ"" ୟୁଜର୍ ଆପ୍ଲିକେଶନ୍ ଏବଂ ତାହାର ଡାଟା ବାହାର କରିଦିଆଯିବ।"
+ "ୟୁଜର୍ %1$sଙ୍କ ପାଇଁ ଆପଣ ଏହି ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବେ କି?"
+ "ଏହି ଆପ୍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା କାଢ଼ିଦିଆଯିବ।"
+ "ଏହି ଆପ୍ ଫ୍ୟାକ୍ଟୋରୀ ଭର୍ସନ୍ ସହ ବଦଳାଇବେ? ସମସ୍ତ ଡାଟା ବାହାର କରିଦିଆଯିବ। ୱର୍କ ପ୍ରୋଫାଇଲ୍ ଥିବା ସମେତ, ଏହାଦ୍ୱାରା ଡିଭାଇସରେ ଥିବା ସମସ୍ତ ୟୁଜର୍ ପ୍ରଭାବିତ ହେବେ।"
+ "ଅନଇନଷ୍ଟଲ୍ ଚାଲୁଛି"
+ "ଅନଇନଷ୍ଟଲ୍ କରାଯାଇ ପାରିଲା ନାହିଁ"
+ "ଅନଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"
+ "%1$s ଅନଇନଷ୍ଟଲ୍ କରାଯାଉଛି…"
+ "ଅନଇନଷ୍ଟଲ୍ ହୋଇଗଲା।"
+ "%1$s ଅନଇନଷ୍ଟଲ୍ କରାଗଲା"
+ "ଅନଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ।"
+ "%1$s ଅନଇନଷ୍ଟଲ୍ କରିବା ସଫଳ ହେଲାନାହିଁ।"
+ "ସକ୍ରିୟ ଡିଭାଇସ୍ ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରିପାରିବ ନାହିଁ"
+ "%1$sଙ୍କ ପାଇଁ ସକ୍ରିୟ ଡିଭାଇସ୍ ଆଡମିନ୍ ଆପ୍ ଅନଇନଷ୍ଟଲ୍ କରାଯାଇ ପାରିବ ନାହିଁ"
+ "କିଛି ୟୁଜର୍ କିମ୍ବା ପ୍ରୋଫାଇଲ୍ ପାଇଁ ଏହି ଆପ୍ ଆବଶ୍ୟକ ଏବଂ ଅନ୍ୟ ସମସ୍ତଙ୍କ ପାଇଁ ଅନଇନଷ୍ଟଲ୍ କରାଯାଇଥିଲା"
+ "ଏହି ଆପ୍ ଆପଣଙ୍କ ପ୍ରୋଫାଇଲ୍ ପାଇଁ ଆବଶ୍ୟକ ଅଟେ ଏବଂ ଅନଇନଷ୍ଟଲ୍ କରାଯାଇପାରିବ ନାହିଁ।"
+ "ଆପଣଙ୍କ ଡିଭାଇସ୍ ଆଡମିନିଷ୍ଟ୍ରେଟର୍ ଏହି ଆପ୍ ଆବଶ୍ୟକ କରନ୍ତୁ ଏବଂ ଏହାକୁ ଅନ୍ଇନଷ୍ଟଲ୍ କରାଯାଇପାରିବ ନାହିଁ।"
+ "ଡିଭାଇସ୍ ଆଡମିନ୍ ଆପ୍ ପରିଚାଳନା କରନ୍ତୁ"
+ "ୟୁଜର୍ଙ୍କୁ ପରିଚାଳନା କରନ୍ତୁ"
+ "%1$s ଅନଇନଷ୍ଟଲ୍ କରାଯାଇପାରିଲା ନାହିଁ।"
+ "ଏହି ପ୍ୟାକେଜ୍ ପାର୍ସ କରିବାରେ ସମସ୍ୟା ଥିଲା।"
+ "ନୂଆ"
+ "ସମସ୍ତ"
+ "ଗୋପନୀୟତା"
+ "ଡିଭାଇସ୍ ଆକ୍ସେସ୍"
+ "ଏହି ଅପଡେଟ୍ କୌଣସି ନୂଆ ଅନୁମତି ଆବଶ୍ୟକ କରେନାହିଁ"
+ "ପ୍ରତ୍ୟାଖ୍ୟାନ"
+ "ଅଧିକ ସୂଚନା"
+ "ତଥାପି ଖାରଜ କରନ୍ତୁ"
+ "%2$sଟିରୁ %1$sଟି"
+ "<b>%1$s</b>କୁ %2$s ପାଇଁ ଅନୁମତି ଦେବେ କି?"
+ "<b>%1$s</b> ସବୁବେଳେ %2$sକୁ ଅନୁମତି ଦେବେ?"
+ "କେବଳ ଆପ୍ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ"
+ "ସର୍ବଦା"
+ "ମନାକରନ୍ତୁ ଏବଂ ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"
+ "%1$dଟି ଅକ୍ଷମ କରାଯାଇଛି"
+ "ସମସ୍ତ ଅକ୍ଷମ କରାଯାଇଛି"
+ "କିଛିବି ଅକ୍ଷମ କରାଯାଇନାହିଁ"
+ "ଅନୁମତି ଦିଅନ୍ତୁ"
+ "ଆପ୍"
+ "ଆପ୍ ଅନୁମତି"
+ "ପୁଣି ପଚାରନ୍ତୁ ନାହିଁ"
+ "କୌଣସି ଅନୁମତି ନାହିଁ"
+ "ଅତିରିକ୍ତ ଅନୁମତି"
+ "ଆପ୍ର ତଥ୍ୟ ଖୋଲନ୍ତୁ"
+
+ %1$dଟି ଅଧିକ
+ %1$dଟି ଅଧିକ
+
+ "ଏହି ଆପ୍ Androidର ଏକ ପୁରୁଣା ସଂସ୍କରଣ ପାଇଁ ଡିଜାଇନ୍ କରାଯାଇଥିଲା। ଅନୁମତି ପ୍ରତ୍ୟାଖ୍ୟାନ କରିବା ଦ୍ୱାରା ଏହା ଠିକ୍ ଭାବେ ଆଉ କାମ ନକରିପାରେ।"
+ "ଏକ ଅଜଣା କାମ କରେ"
+ "%2$dଟି ଆପରୁ %1$dଟିକୁ ଅନୁମତି ଦିଆଯାଇଛି"
+ "ସିଷ୍ଟମ୍କୁ ଦେଖାନ୍ତୁ"
+ "ସିଷ୍ଟମ୍କୁ ଲୁଚାନ୍ତୁ"
+ "କୌଣସି ଆପ୍ ନାହିଁ"
+ "ଲୋକେଶନ୍ ସେଟିଙ୍ଗ"
+ "ଏହି ଡିଭାଇସ୍ ପାଇଁ %1$s ଲୋକେଶନ୍ ସେବା ପ୍ରଦାନ କରେ। ଲୋକେଶନ୍ ସେଟିଙ୍ଗରୁ ଲୋକେଶନ୍ ଆକ୍ସେସ୍ ସଂଶୋଧନ କରାଯାଇପାରିବ।"
+ "ଏହି ଅନୁମତିକୁ ଯଦି ଆପଣ ପ୍ରତ୍ୟାଖ୍ୟାନ କରନ୍ତି, ଆପଣଙ୍କ ଡିଭାଇସ୍ର ସାଧାରଣ ସୁବିଧାଗୁଡ଼ିକ ଆଉ କାମ ନକରିପାରେ।"
+ "ପଲିସୀ ଦ୍ୱାରା ଲାଗୁ"
+ "ନିତୀ ଅନୁସାରେ ବ୍ୟାକ୍ଗ୍ରାଉଣ୍ଡ ଆକ୍ସେସ୍ ଅକ୍ଷମ କରାଯାଇଛି"
+ "ନିତୀ ଅନୁସାରେ ପୃଷ୍ଠପଟ ଆକ୍ସେସ୍ ସକ୍ଷମ କରାଯାଇଛି"
+ "ନିତୀ ଅନୁସାରେ ସମ୍ମୁଖଭାଗ ଆକ୍ସେସ୍ ସକ୍ଷମ କରାଯାଇଛି"
+ "ଆଡ୍ମିନ୍ ଦ୍ୱାରା ନିୟନ୍ତ୍ରିତ"
+
+
+
+ "ସର୍ବଦା"
+ "କେବଳ ଆପ୍ ବ୍ୟବହାର କରୁଥିବା ସମୟରେ"
+ "କେବେ ନୁହେଁ"
+ "ଲୋଡ୍ କରାଯାଉଛି…"
+ "ସମସ୍ତ ଅନୁମତି"
+ "ଅନ୍ୟାନ୍ୟ ଆପ୍ ଦକ୍ଷତା"
+ "ଅନୁମତି ଅନୁରୋଧ"
+ "ସ୍କ୍ରୀନ୍ ଓଭର୍ଲେ ଚିହ୍ନଟ ହୋଇଛି"
+ "ଏହି ଅନୁମତି ସେଟିଙ୍ଗ ବଦଳାଇବାକୁ, ପ୍ରଥମେ ଆପଣ ସେଟିଙ୍ଗ ଓ ଆପ୍ରୁ ସ୍କ୍ରୀନ୍ ଅଫ୍ କରନ୍ତୁ"
+ "ସେଟିଙ୍ଗ ଖୋଲନ୍ତୁ"
+ "Android ୱିୟର୍"
+ "Wearରେ ଇନଷ୍ଟଲ୍/ଅନଇନଷ୍ଟଲ୍ କାର୍ଯ୍ୟ ସପୋର୍ଟ କରେ ନାହିଁ।"
+ "<b>%1$s</b> ଆକ୍ସେସ୍ କରିବା ପାଇଁ କ\'ଣ କ\'ଣ ଅନୁମତି ଦିଆଯିବ, ତାହା ବାଛନ୍ତୁ"
+ "<b>%1$s</b> ଅପଡେଟ୍ କରାଯାଇଛି। ଏହି ଆପ୍ କ\'ଣ କ\'ଣ ଆକ୍ସେସ୍ କରିପାରିବ, ତାହା ବାଛନ୍ତୁ।"
+ "କ୍ୟାନ୍ସଲ୍ କରନ୍ତୁ"
+ "ଜାରି ରଖନ୍ତୁ"
+ "ନୂଆ ଅନୁମତି"
+ "ବର୍ତ୍ତମାନର ଅନୁମତି"
+ "ଆପ୍ ପର୍ଯ୍ୟାୟଭୁକ୍ତ କରାଯାଉଛି…"
+ "ଅଜଣା"
+ "ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟାବଲେଟକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"
+ "ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଟିଭିକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"
+ "ଆପଣଙ୍କ ସୁରକ୍ଷା ପାଇଁ, ଆପଣଙ୍କ ଫୋନକୁ ଏହି ସୋର୍ସରୁ ଆସିଥିବା ଅଜଣା ଆପ୍ ଇନଷ୍ଟଲ୍ କରିବାକୁ ଅନୁମତି ଦିଆଯାଇ ନାହିଁ।"
+ "ଅଜଣା ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ଫୋନ୍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଫୋନରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"
+ "ଅଜଣା ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ଟାବଲେଟ୍ ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଟାବଲେଟରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"
+ "ଅଜଣା ଆପ୍ ଦ୍ୱାରା ଆପଣଙ୍କ ଟିଭି ଏବଂ ବ୍ୟକ୍ତିଗତ ଡାଟାକୁ ନଷ୍ଟ କରାଯାଇ ପାରିବାର ସମ୍ଭାବନା ବହୁତ ଅଧିକ। ଏହି ଆପଜୁ ଇନଷ୍ଟଲ୍ କରିବାର ଅର୍ଥ, ଆପଣଙ୍କ ଟିଭିରେ ଘଟିବା କୌଣସି ପ୍ରକାର କ୍ଷତି କିମ୍ବା ସେଗୁଡ଼ିକର ବ୍ୟବହାରରୁ ହେବା କୌଣସି ପ୍ରକାର ଡାଟାର ହାନୀ ପାଇଁ ଆପଣ ଦାୟୀ ରହିବାକୁ ରାଜି ହୁଅନ୍ତି।"
+ "ଜାରି ରଖନ୍ତୁ"
+ "ସେଟିଙ୍ଗ"
+ "ୱେର୍ ଆପ୍ ଇନଷ୍ଟଲ୍/ଅନଇନଷ୍ଟଲ୍ କରାଯାଉଛି"
+
diff --git a/packages/PackageInstaller/res/values-pa-television/strings.xml b/packages/PackageInstaller/res/values-pa-television/strings.xml
new file mode 100644
index 0000000000000..76d249c8a5605
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ਅਸਵੀਕਾਰ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"
+ "ਤੁਸੀਂ ਇਸਨੂੰ ਬਾਅਦ ਵਿੱਚ ਸੈਟਿੰਗਾਂ > ਐਪਾਂ ਵਿੱਚ ਬਦਲ ਸਕਦੇ ਹੋ"
+ "%1$s / %2$s"
+ "ਸਿਸਟਮ ਐਪਸ ਦਿਖਾਓ"
+ "ਐਪ ਇਜਾਜ਼ਤਾਂ"
+ "ਐਪ ਇਜਾਜ਼ਤਾਂ"
+ "%1$s ਇਜਾਜ਼ਤਾਂ"
+ "ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ"
+ "%1$s ਇਜਾਜ਼ਤਾਂ"
+
diff --git a/packages/PackageInstaller/res/values-pa-watch/strings.xml b/packages/PackageInstaller/res/values-pa-watch/strings.xml
new file mode 100644
index 0000000000000..e0930212914e0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ਅਸਵੀਕਾਰ ਕਰੋ, ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"
+ "%1$s / %2$s"
+ "ਸਿਸਟਮ ਐਪਸ ਦਿਖਾਓ"
+ "ਬਦਲਿਆ ਨਹੀਂ ਜਾ ਸਕਦਾ"
+ "ਹਾਂ"
+ "ਰੱਦ ਕਰੋ"
+
diff --git a/packages/PackageInstaller/res/values-pa/strings.xml b/packages/PackageInstaller/res/values-pa/strings.xml
new file mode 100644
index 0000000000000..b364c78ae4da5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pa/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ਪੈਕੇਜ ਸਥਾਪਨਾਕਾਰ"
+ "ਅੱਗੇ"
+ "ਸਥਾਪਤ ਕਰੋ"
+ "ਹੋ ਗਿਆ"
+ "ਰੱਦ ਕਰੋ"
+ "ਇੰਸਟੌਲ ਕਰ ਰਿਹਾ ਹੈ…"
+ "%1$s ਨੂੰ ਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"
+ "ਐਪ ਇੰਸਟੌਲ ਕੀਤਾ।"
+ "ਕੀ ਤੁਸੀਂ ਇਹ ਐਪਲੀਕੇਸ਼ਨ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਹ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗਾ:"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਐਪਲੀਕੇਸ਼ਨ ਨੂੰ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਮੌਜੂਦਾ ਐਪਲੀਕੇਸ਼ਨ ਤੇ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਕੀਤਾ ਜਾਏਗਾ। ਅੱਪਡੇਟ ਕੀਤੀ ਐਪਲੀਕੇਸ਼ਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗੀ:"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਬਿਲਟ-ਇਨ ਐਪਲੀਕੇਸ਼ਨ ਤੇ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਕੀਤਾ ਜਾਏਗਾ। ਅੱਪਡੇਟ ਕੀਤੀ ਐਪਲੀਕੇਸ਼ਨ ਇਸ ਤੱਕ ਪਹੁੰਚ ਪ੍ਰਾਪਤ ਕਰੇਗੀ:"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਮੌਜੂਦਾ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਨਹੀਂ ਹੋਵੇਗਾ। ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਬਿਲਟ-ਇਨ ਐਪਲੀਕੇਸ਼ਨ ਵਿੱਚ ਇੱਕ ਅੱਪਡੇਟ ਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਤੁਹਾਡਾ ਮੌਜੂਦਾ ਡਾਟਾ ਨਸ਼ਟ ਨਹੀਂ ਹੋਵੇਗਾ। ਇਸ ਲਈ ਕਿਸੇ ਖਾਸ ਪਹੁੰਚ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"
+ "ਐਪ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ।"
+ "ਪੈਕੇਜ ਨੂੰ ਸਥਾਪਿਤ ਹੋਣ ਤੋਂ ਬਲੌਕ ਕੀਤਾ ਗਿਆ ਸੀ।"
+ "ਪੈਕੇਜ ਦੇ ਇੱਕ ਮੌਜੂਦਾ ਪੈਕੇਜ ਨਾਲ ਵਿਵਾਦ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"
+ "ਐਪ ਦੇ ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ ਦੇ ਅਨੁਰੂਪ ਨਾ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"
+ "ਇਹ ਐਪ ਤੁਹਾਡੇ ਟੀਵੀ ਦੇ ਅਨੁਕੂਲ ਨਹੀਂ ਹੈ।"
+ "ਐਪ ਦੇ ਤੁਹਾਡੇ ਫ਼ੋਨ ਦੇ ਅਨੁਰੂਪ ਨਾ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"
+ "ਪੈਕੇਜ ਦੇ ਅਵੈਧ ਪ੍ਰਤੀਤ ਹੋਣ ਕਰਕੇ ਐਪ ਦੀ ਸਥਾਪਨਾ ਨਹੀਂ ਕੀਤੀ ਗਈ।"
+ "%1$s ਨੂੰ ਤੁਹਾਡੀ ਟੈਬਲੈੱਟ ਤੇ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"
+ "%1$s ਨੂੰ ਤੁਹਾਡੇ TV ਤੇ ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"
+ "%1$s ਨੂੰ ਤੁਹਾਡੇ ਫ਼ੋਨ ਤੇ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"
+ "ਖੋਲ੍ਹੋ"
+ "ਤੁਹਾਡਾ ਪ੍ਰਸ਼ਾਸਕ ਅਗਿਆਤ ਸਰੋਤਾਂ ਤੋਂ ਪ੍ਰਾਪਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੰਦਾ"
+ "ਇਸ ਵਰਤੋਂਕਾਰ ਵੱਲੋਂ ਅਗਿਆਤ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"
+ "ਇਸ ਵਰਤੋਂਕਾਰ ਨੂੰ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ"
+ "ਠੀਕ"
+ "ਐਪਸ ਵਿਵਸਥਿਤ ਕਰੋ"
+ "ਖਾਲੀ ਸਪੇਸ"
+ "%1$s ਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ। ਕੁਝ ਸਪੇਸ ਖਾਲੀ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਕੋਸ਼ਿਸ਼ ਕਰੋ।"
+ "ਐਪ ਨਹੀਂ ਮਿਲਿਆ"
+ "ਐਪ ਇੰਸਟੌਲ ਕੀਤੇ ਐਪਸ ਦੀ ਸੂਚੀ ਵਿੱਚ ਨਹੀਂ ਮਿਲਿਆ ਸੀ।"
+ "ਇਜਾਜ਼ਤ ਨਹੀਂ"
+ "ਮੌਜੂਦਾ ਵਰਤੋਂਕਾਰ ਨੂੰ ਇਹ ਅਣਸਥਾਪਨਾ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਦਿੱਤੀ ਗਈ ਹੈ।"
+ "ਗੜਬੜ"
+ "ਐਪ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤੀ ਜਾ ਸਕੀ।"
+ "ਐਪ ਅਣਸਥਾਪਤ ਕਰੋ"
+ "ਅੱਪਡੇਟ ਅਣਸਥਾਪਤ ਕਰੋ"
+ "%1$s ਇਸ ਐਪ ਦਾ ਭਾਗ ਹੈ:"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"
+ "ਕੀ ਤੁਸੀਂ ਇਸ ਐਪ ਨੂੰ ""ਸਾਰੇ"" ਵਰਤੋਂਕਾਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ? ਐਪਲੀਕੇਸ਼ਨ ਅਤੇ ਇਸਦਾ ਡਾਟਾ ਡੀਵਾਈਸ ਤੇ ""ਸਾਰੇ"" ਵਰਤੋਂਕਾਰਾਂ ਵੱਲੋਂ ਹਟਾ ਦਿੱਤਾ ਜਾਏਗਾ।"
+ "ਕੀ ਤੁਸੀਂ ਵਰਤੋਂਕਾਰ %1$s ਲਈ ਇਸ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਚਾਹੁੰਦੇ ਹੋ?"
+ "ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਸੰਸਕਰਣ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ ਡਾਟਾ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ।"
+ "ਕੀ ਇਸ ਐਪ ਨੂੰ ਫੈਕਟਰੀ ਵਰਜਨ ਨਾਲ ਬਦਲਣਾ ਹੈ? ਸਾਰਾ ਡਾਟਾ ਹਟਾ ਦਿੱਤਾ ਜਾਵੇਗਾ। ਇਹ ਇਸ ਡੀਵਾਈਸ ਦੇ ਸਾਰੇ ਵਰਤੋਂਕਾਰਾਂ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰੇਗਾ, ਜਿਸ ਵਿੱਚ ਕਾਰਜ ਪ੍ਰੋਫਾਈਲ ਵਾਲੇ ਵਰਤੋਂਕਾਰ ਵੀ ਸ਼ਾਮਲ ਹਨ।"
+ "ਚੱਲ ਰਹੀਆਂ ਅਣਸਥਾਪਨਾਵਾਂ"
+ "ਅਸਫਲ ਅਣਸਥਾਪਨਾਵਾਂ"
+ "ਅਣਇੰਸਟੌਲ ਕਰ ਰਿਹਾ ਹੈ…"
+ "%1$s ਨੂੰ ਅਣਸਥਾਪਤ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"
+ "ਅਣਸਥਾਪਨਾ ਪੂਰੀ ਹੋਈ।"
+ "%1$s ਨੂੰ ਅਣਸਥਾਪਤ ਕੀਤਾ"
+ "ਅਣਸਥਾਪਨਾ ਅਸਫਲ।"
+ "%1$s ਨੂੰ ਅਣਸਥਾਪਤ ਕਰਨਾ ਅਸਫਲ ਰਿਹਾ।"
+ "ਕਿਰਿਆਸ਼ੀਲ ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"
+ "%1$s ਲਈ ਕਿਰਿਆਸ਼ੀਲ ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ"
+ "ਇਹ ਐਪ ਕੁਝ ਵਰਤੋਂਕਾਰਾਂ ਜਾਂ ਪ੍ਰੋਫਾਈਲਾਂ ਲਈ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਹੋਰਾਂ ਲਈ ਅਣਸਥਾਪਤ ਕੀਤੀ ਗਈ ਸੀ"
+ "ਇਹ ਐਪ ਤੁਹਾਡੀ ਪ੍ਰੋਫਾਈਲ ਲਈ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਇਸ ਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"
+ "ਇਹ ਐਪ ਤੁਹਾਡੇ ਡੀਵਾਈਸ ਪ੍ਰਬੰਧਕ ਵੱਲੋਂ ਲੋੜੀਂਦੀ ਹੈ ਅਤੇ ਇਸਨੂੰ ਅਣਸਥਾਪਤ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਦਾ।"
+ "ਡੀਵਾਈਸ ਪ੍ਰਸ਼ਾਸਕ ਐਪਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"
+ "ਵਰਤੋਂਕਾਰਾਂ ਦਾ ਪ੍ਰਬੰਧਨ ਕਰੋ"
+ "%1$s ਅਣਇੰਸਟੌਲ ਨਹੀਂ ਕੀਤਾ ਜਾ ਸਕਿਆ।"
+ "ਪੈਕੇਜ ਨੂੰ ਪਾਰਸ ਕਰਨ ਵਿੱਚ ਇੱਕ ਸਮੱਸਿਆ ਸੀ।"
+ "ਨਵਾਂ"
+ "ਸਭ"
+ "ਪ੍ਰਾਈਵੇਸੀ"
+ "ਡੀਵਾਈਸ ਪਹੁੰਚ"
+ "ਇਸ ਅੱਪਡੇਟ ਲਈ ਕਿਸੇ ਨਵੀਆਂ ਅਨੁਮਤੀਆਂ ਦੀ ਲੋੜ ਨਹੀਂ ਹੈ।"
+ "ਅਸਵੀਕਾਰ ਕਰੋ"
+ "ਹੋਰ ਜਾਣਕਾਰੀ"
+ "ਫੇਰ ਵੀ ਅਸਵੀਕਾਰ ਕਰੋ"
+ "%2$s ਦਾ %1$s"
+ "ਕੀ <b>%1$s</b> ਨੂੰ %2$s ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ?"
+ "ਹਮੇਸ਼ਾਂ <b>%1$s</b> ਨੂੰ %2$s ਕਰਨ ਦਿਓ?"
+ "ਸਿਰਫ਼ ਐਪ ਵਰਤਣ ਵੇਲੇ"
+ "ਹਮੇਸ਼ਾਂ"
+ "ਅਸਵੀਕਾਰ ਕਰੋ ਅਤੇ ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"
+ "%1$d ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"
+ "ਸਭ ਨੂੰ ਅਯੋਗ ਬਣਾਇਆ ਗਿਆ ਹੈ"
+ "ਕਿਸੇ ਨੂੰ ਵੀ ਅਯੋਗ ਨਹੀਂ ਬਣਾਇਆ ਗਿਆ ਹੈ"
+ "ਆਗਿਆ ਦਿਓ"
+ "ਐਪਾਂ"
+ "ਐਪ ਇਜਾਜ਼ਤਾਂ"
+ "ਦੁਬਾਰਾ ਨਾ ਪੁੱਛੋ"
+ "ਕੋਈ ਇਜਾਜ਼ਤਾਂ ਨਹੀਂ ਹਨ"
+ "ਵਧੀਕ ਇਜਾਜ਼ਤਾਂ"
+ "ਐਪ ਜਾਣਕਾਰੀ ਖੋਲ੍ਹੋ"
+
+ %1$d ਹੋਰ
+ %1$d ਹੋਰ
+
+ "ਇਹ ਐਪ Android ਦੇ ਕਿਸੇ ਪੁਰਾਣੇ ਸੰਸਕਰਣ ਲਈ ਬਣਾਈ ਗਈ ਸੀ। ਅਨੁਮਤੀ ਨੂੰ ਇਨਕਾਰ ਕਰਨਾ ਇਸਦੇ ਉਦੇਸ਼ਿਤ ਫੰਕਸ਼ਨ ਨੂੰ ਪ੍ਰਭਾਵਿਤ ਕਰ ਸਕਦਾ ਹੈ।"
+ "ਕੋਈ ਅਗਿਆਤ ਕਾਰਵਾਈ ਕਰੋ"
+ "%2$d ਵਿੱਚੋਂ %1$d ਐਪਾਂ ਨੂੰ ਆਗਿਆ ਦਿੱਤੀ"
+ "ਸਿਸਟਮ ਦਿਖਾਓ"
+ "ਸਿਸਟਮ ਲੁਕਾਓ"
+ "ਕੋਈ ਐਪਾਂ ਨਹੀਂ"
+ "ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ"
+ "%1$s ਇਸ ਡੀਵਾਈਸ ਲਈ ਟਿਕਾਣਾ ਸੇਵਾਵਾਂ ਦਾ ਇੱਕ ਪ੍ਰਦਾਤਾ ਹੈ। ਟਿਕਾਣਾ ਪਹੁੰਚ ਨੂੰ ਟਿਕਾਣਾ ਸੈਟਿੰਗਾਂ ਤੋਂ ਸੰਸ਼ੋਧਿਤ ਕੀਤਾ ਜਾ ਸਕਦਾ ਹੈ।"
+ "ਜੇਕਰ ਤੁਸੀਂ ਇਸ ਇਜਾਜ਼ਤ ਨੂੰ ਅਸਵੀਕਾਰ ਕਰਦੇ ਹੋ, ਤਾਂ ਤੁੁਹਾਡੇ ਡੀਵਾਈਸ ਦੀਆਂ ਮੂੂਲ ਵਿਸ਼ੇਸ਼ਤਾਵਾਂ ਆਪਣੇ ਫੰਕਸ਼ਨ ਮੁਤਾਬਕ ਕੰਮ ਨਹੀਂ ਵੀ ਕਰ ਸਕਦੀਆਂ।"
+ "ਨੀਤੀ ਮੁਤਾਬਕ ਲਾਗੂ ਕੀਤਾ"
+ "ਨੀਤੀ ਵੱਲੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਬੰਦ ਕੀਤਾ ਗਿਆ"
+ "ਨੀਤੀ ਵੱਲੋਂ ਬੈਕਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"
+ "ਨੀਤੀ ਵੱਲੋਂ ਫੋਰਗ੍ਰਾਊਂਡ ਪਹੁੰਚ ਨੂੰ ਚਾਲੂ ਕੀਤਾ ਗਿਆ"
+ "ਪ੍ਰਸ਼ਾਸਕ ਵੱਲੋਂ ਕੰਟਰੋਲ ਕੀਤੀ ਗਈ"
+
+
+
+ "ਹਮੇਸ਼ਾਂ"
+ "ਸਿਰਫ਼ ਐਪ ਵਰਤਣ ਦੌਰਾਨ"
+ "ਕਦੇ ਵੀ ਨਹੀਂ"
+ "ਲੋਡ ਕਰ ਰਿਹਾ ਹੈ…"
+ "ਸਾਰੀਆਂ ਅਨੁਮਤੀਆਂ"
+ "ਹੋਰ ਐਪ ਸਮਰੱਥਤਾਵਾਂ"
+ "ਇਜਾਜ਼ਤ ਬੇਨਤੀ"
+ "ਸਕਰੀਨ ਓਵਰਲੇਅ ਲੱਭ ਗਿਆ"
+ "ਇਸ ਇਜ਼ਾਜਤ ਸੈਟਿੰਗ ਨੂੰ ਬਦਲਣ ਲਈ; ਤੁਹਾਨੂੰ ਪਹਿਲਾਂ ਸੈਟਿੰਗਾਂ ਅਤੇ ਐਪਾਂ ਤੋਂ ਸਕ੍ਰੀਨ ਓਵਰਲੇਅ ਬੰਦ ਕਰਨਾ ਪਵੇਗਾ"
+ "ਸੈਟਿੰਗਾਂ ਖੋਲ੍ਹੋ"
+ "Android Wear"
+ "ਵੀਅਰ \'ਤੇ ਸਥਾਪਤ/ਅਣਸਥਾਪਤ ਕਾਰਵਾਈਆਂ ਸਮਰਥਿਤ ਨਹੀਂ ਹਨ।"
+ "ਇਹ ਚੁਣੋ ਕਿ <b>%1$s</b> ਨੂੰ ਕਿਸ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ"
+ "<b>%1$s</b> ਨੂੰ ਅੱਪਡੇਟ ਕੀਤਾ ਜਾ ਚੁੱਕਿਆ ਹੈ। ਇਹ ਚੁਣੋ ਕਿ ਇਸ ਐਪ ਨੂੰ ਕਿਸ \'ਤੇ ਪਹੁੰਚ ਕਰਨ ਦੀ ਆਗਿਆ ਦੇਣੀ ਹੈ।"
+ "ਰੱਦ ਕਰੋ"
+ "ਜਾਰੀ ਰੱਖੋ"
+ "ਨਵੀਆਂ ਇਜਾਜ਼ਤਾਂ"
+ "ਵਰਤਮਾਨ ਇਜਾਜ਼ਤਾਂ"
+ "ਐਪ ਨੂੰ ਪੜਾਅਬੱਧ ਕੀਤਾ ਜਾ ਰਿਹਾ ਹੈ…"
+ "ਅਗਿਆਤ"
+ "ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਟੈਬਲੈੱਟ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"
+ "ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਟੀਵੀ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"
+ "ਤੁਹਾਡੀ ਸੁਰੱਖਿਆ ਲਈ, ਤੁਹਾਡੇ ਫ਼ੋਨ ਨੂੰ ਇਸ ਸਰੋਤ ਤੋਂ ਅਗਿਆਤ ਐਪਾਂ ਸਥਾਪਤ ਕਰਨ ਦੀ ਇਜਾਜ਼ਤ ਨਹੀਂ ਹੈ।"
+ "ਤੁਹਾਡਾ ਫ਼ੋਨ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਫ਼ੋਨ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"
+ "ਤੁਹਾਡਾ ਟੈਬਲੈੱਟ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਕਮਜ਼ੋਰ ਹੈ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੈਬਲੈੱਟ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"
+ "ਤੁਹਾਡਾ ਟੀਵੀ ਅਤੇ ਨਿੱਜੀ ਡਾਟਾ ਅਗਿਆਤ ਐਪਾਂ ਤੋਂ ਹਮਲੇ ਪ੍ਰਤੀ ਵਧੇਰੇ ਵਿੰਨਣਸ਼ੀਲ ਹਨ। ਇਹ ਐਪ ਸਥਾਪਤ ਕਰਕੇ, ਤੁਸੀਂ ਸਹਿਮਤੀ ਦਿੰਦੇ ਹੋ ਕਿ ਆਪਣੇ ਟੀਵੀ ਨੂੰ ਹੋਣ ਵਾਲੇ ਕਿਸੇ ਵੀ ਨੁਕਸਾਨ ਜਾਂ ਡਾਟੇ ਦੀ ਹਾਨੀ ਲਈ ਤੁਸੀਂ ਜ਼ਿੰਮੇਵਾਰ ਹੋ ਜੋ ਸ਼ਾਇਦ ਇਸ ਐਪ ਨੂੰ ਵਰਤਣ ਦੇ ਨਤੀਜੇ ਵਜੋਂ ਹੋ ਸਕਦਾ ਹੈ।"
+ "ਜਾਰੀ ਰੱਖੋ"
+ "ਸੈਟਿੰਗਾਂ"
+ "ਵੀਅਰ ਐਪਾਂ ਨੂੰ ਸਥਾਪਤ/ਅਣਸਥਾਪਤ ਕਰਨਾ"
+
diff --git a/packages/PackageInstaller/res/values-pl-television/strings.xml b/packages/PackageInstaller/res/values-pl-television/strings.xml
new file mode 100644
index 0000000000000..6fede9ae22ac1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Odmów i nie pytaj ponownie"
+ "Możesz to później zmienić, wybierając Ustawienia > Aplikacje"
+ "%1$s/%2$s"
+ "Pokaż aplikacje systemowe"
+ "Uprawnienia aplikacji"
+ "Uprawnienia aplikacji"
+ "%1$s – uprawnienia"
+ "Dodatkowe uprawnienia"
+ "%1$s – uprawnienia"
+
diff --git a/packages/PackageInstaller/res/values-pl-watch/strings.xml b/packages/PackageInstaller/res/values-pl-watch/strings.xml
new file mode 100644
index 0000000000000..95c368751bca4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Odmów i nie pytaj ponownie"
+ "%1$s / %2$s"
+ "Pokaż aplikacje systemowe"
+ "Nie można zmienić"
+ "Tak"
+ "Anuluj"
+
diff --git a/packages/PackageInstaller/res/values-pl/strings.xml b/packages/PackageInstaller/res/values-pl/strings.xml
new file mode 100644
index 0000000000000..dbedfad30d593
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pl/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Instalator pakietu"
+ "Dalej"
+ "Instaluj"
+ "Gotowe"
+ "Anuluj"
+ "Instalowanie..."
+ "Instaluję pakiet %1$s…"
+ "Aplikacja została zainstalowana."
+ "Zainstalować tę aplikację? Będzie miała następujące uprawnienia:"
+ "Zainstalować tę aplikację? Nie ma specjalnych wymagań dotyczących dostępu."
+ "Zainstalować aktualizację tej aplikacji? Nie utracisz wcześniejszych danych. Zaktualizowana aplikacja będzie miała następujące uprawnienia:"
+ "Zainstalować aktualizację fabrycznej aplikacji? Nie utracisz wcześniejszych danych. Zaktualizowana aplikacja będzie miała następujące uprawnienia:"
+ "Chcesz zaktualizować tę istniejącą aplikację? Nie utracisz danych. Nie są wymagane specjalne uprawnienia dostępu."
+ "Chcesz zaktualizować tę wbudowaną aplikację? Nie utracisz danych. Nie są wymagane specjalne uprawnienia dostępu."
+ "Aplikacja nie została zainstalowana."
+ "Instalacja pakietu została zablokowana."
+ "Aplikacja nie została zainstalowana, bo powoduje konflikt z istniejącym pakietem."
+ "Aplikacja nie została zainstalowana, bo jest niezgodna z Twoim tabletem."
+ "Aplikacja jest niezgodna z Twoim telewizorem."
+ "Aplikacja nie została zainstalowana, bo jest niezgodna z Twoim telefonem."
+ "Aplikacja nie została zainstalowana, bo pakiet wygląda na nieprawidłowy."
+ "Nie można zainstalować aplikacji %1$s na Twoim tablecie."
+ "Nie udało się zainstalować %1$s na Twoim telewizorze."
+ "Nie można zainstalować aplikacji %1$s na Twoim telefonie."
+ "Otwórz"
+ "Twój administrator nie zezwala na instalowanie aplikacji pochodzących z nieznanych źródeł."
+ "Ten użytkownik nie może instalować nieznanych aplikacji"
+ "Ten użytkownik nie może instalować aplikacji"
+ "OK"
+ "Zarządzaj aplikacjami"
+ "Brak miejsca"
+ "Nie można zainstalować aplikacji %1$s. Zwolnij trochę miejsca i spróbuj ponownie."
+ "Nie znaleziono aplikacji"
+ "Aplikacji nie znaleziono na liście zainstalowanych programów."
+ "Niedozwolone"
+ "Bieżący użytkownik nie może tego odinstalować."
+ "Błąd"
+ "Nie można odinstalować aplikacji."
+ "Odinstaluj aplikację"
+ "Odinstaluj aktualizację"
+ "%1$s jest częścią następującej aplikacji:"
+ "Odinstalować tę aplikację?"
+ "Chcesz odinstalować tę aplikację dla ""wszystkich"" użytkowników? Ta aplikacja i jej dane zostaną usunięte dla ""wszystkich"" użytkowników na urządzeniu."
+ "Chcesz odinstalować tę aplikację dla użytkownika %1$s?"
+ "Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte."
+ "Przywrócić fabryczną wersję tej aplikacji? Wszystkie dane zostaną usunięte. Dotyczy to wszystkich użytkowników tego urządzenia, również tych korzystających z profilu do pracy."
+ "Aktywne odinstalowania"
+ "Nieudane odinstalowania"
+ "Odinstalowywanie..."
+ "Odinstalowuję pakiet %1$s…"
+ "Odinstalowywanie zakończone"
+ "Odinstalowano pakiet %1$s"
+ "Nie udało się odinstalować."
+ "Nie udało się odinstalować pakietu %1$s."
+ "Nie można odinstalować aktywnej aplikacji do administrowania urządzeniem"
+ "Nie można odinstalować aplikacji do administrowania urządzeniem aktywnej dla użytkownika %1$s"
+ "Niektórzy użytkownicy i niektóre profile wymagają tej aplikacji, a w innych przypadkach została odinstalowana"
+ "Ta aplikacja jest potrzebna w Twoim profilu i nie można jej odinstalować."
+ "Administrator urządzenia wymaga tej aplikacji i nie można jej odinstalować."
+ "Zarządzaj aplikacjami do administrowania urządzeniem"
+ "Zarządzaj użytkownikami"
+ "Nie można odinstalować aplikacji %1$s."
+ "Podczas analizowania pakietu wystąpił problem."
+ "Nowe"
+ "Wszystkie"
+ "Prywatność"
+ "Dostęp do urządzenia"
+ "Ta aktualizacja nie wymaga nowych uprawnień."
+ "Odmów"
+ "Więcej informacji"
+ "Odmów mimo to"
+ "%1$s z %2$s"
+ "Zezwolić aplikacji <b>%1$s</b> na %2$s?"
+ "Zawsze zezwalać aplikacji <b>%1$s</b> na: %2$s?"
+ "Tylko przy używaniu aplikacji"
+ "Zawsze"
+ "Odmów i nie pytaj ponownie"
+ "wyłączone: %1$d"
+ "wszystkie wyłączone"
+ "brak wyłączonych"
+ "Zezwól"
+ "Aplikacje"
+ "Uprawnienia aplikacji"
+ "Nie pytaj ponownie"
+ "Brak uprawnień"
+ "Dodatkowe uprawnienia"
+ "Otwórz informacje o aplikacji"
+
+ Jeszcze %1$d
+ Jeszcze %1$d
+ Jeszcze %1$d
+ Jeszcze %1$d
+
+ "Ta aplikacja jest na straszą wersję Androida. Jeśli odmówisz uprawnień, aplikacja może nie działać prawidłowo."
+ "wykonywanie nieznanych działań"
+ "Dostęp ma: %1$d z %2$d aplikacji"
+ "Pokaż systemowe"
+ "Ukryj systemowe"
+ "Brak aplikacji"
+ "Ustawienia lokalizacji"
+ "%1$s jest dostawcą usług lokalizacyjnych dla tego urządzenia. Dostęp do danych lokalizacji można zmienić w ustawieniach lokalizacji."
+ "Jeśli nie przyznasz tych uprawnień, podstawowe funkcje urządzenia mogą nie działać prawidłowo."
+ "Narzucone przez zasady"
+ "Dostęp w tle wyłączony przez zasady"
+ "Dostęp w tle włączony przez zasady"
+ "Dostęp na pierwszym planie włączony przez zasady"
+ "Kontrolowane przez administratora"
+
+
+
+ "Zawsze"
+ "Tylko przy używaniu aplikacji"
+ "Nigdy"
+ "Ładuję…"
+ "Wszystkie uprawnienia"
+ "Inne funkcje aplikacji"
+ "Prośba o pozwolenie"
+ "Wykryto nakładkę ekranową"
+ "Aby zmodyfikować te uprawnienia, musisz najpierw wyłączyć nakładkę ekranową, klikając Ustawienia > Aplikacje"
+ "Otwórz ustawienia"
+ "Android Wear"
+ "Wear nie obsługuje instalowania ani odinstalowywania."
+ "Wybierz, jakie uprawnienia dostępu ma mieć <b>%1$s</b>"
+ "Aplikacja <b>%1$s</b> została zaktualizowana. Wybierz dla niej uprawnienia dostępu."
+ "Anuluj"
+ "Dalej"
+ "Nowe uprawnienia"
+ "Aktualne uprawnienia"
+ "Przygotowuję aplikację…"
+ "Nieznana"
+ "Ze względów bezpieczeństwa na Twoim tablecie nie można instalować nieznanych aplikacji z tego źródła."
+ "Ze względów bezpieczeństwa na Twoim telewizorze nie można instalować nieznanych aplikacji z tego źródła."
+ "Ze względów bezpieczeństwa na Twoim telefonie nie można instalować nieznanych aplikacji z tego źródła."
+ "Dane na telefonie i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie telefonu lub utratę danych w wyniku jej używania."
+ "Dane na tablecie i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie tabletu lub utratę danych w wyniku jej używania."
+ "Dane na telewizorze i prywatne są bardziej narażone na atak nieznanych aplikacji. Instalując tę aplikację, bierzesz na siebie odpowiedzialność za ewentualne uszkodzenie telewizora lub utratę danych w wyniku jej używania."
+ "Dalej"
+ "Ustawienia"
+ "Instalowanie/odinstalowywanie aplikacji na Wear"
+
diff --git a/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml b/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml
new file mode 100644
index 0000000000000..ba6e68f3d933d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Negar e não perguntar novamente"
+ "É possível alterar isso mais tarde em \"Config.\" > \"Apps\""
+ "%1$s / %2$s"
+ "Mostrar apps do sistema"
+ "Permissões do app"
+ "Permissões do app"
+ "Permissões para %1$s"
+ "Outras permissões"
+ "Permissões para %1$s"
+
diff --git a/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml b/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml
new file mode 100644
index 0000000000000..8742c2db4500c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Negar e não perguntar de novo"
+ "%1$s / %2$s"
+ "Mostrar apps do sistema"
+ "Impossível alterar"
+ "Sim"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-pt-rBR/strings.xml b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
new file mode 100644
index 0000000000000..feb2337bea96a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rBR/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instalador do pacote"
+ "Próximo"
+ "Instalar"
+ "Concluído"
+ "Cancelar"
+ "Instalando..."
+ "Instalando %1$s…"
+ "App instalado."
+ "Quer instalar este app? Ele terá acesso a:"
+ "Quer instalar este app? Não requer acesso especial."
+ "Quer instalar uma atualização para este app? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"
+ "Quer instalar uma atualização para este app integrado? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"
+ "Quer instalar uma atualização para este app existente? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."
+ "Quer instalar uma atualização para este app integrado? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."
+ "O app não foi instalado."
+ "A instalação do pacote foi bloqueada."
+ "Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."
+ "Como o app não é compatível com seu tablet, ele não foi instalado."
+ "Este app não é compatível com sua TV."
+ "Como o app não é compatível com seu smartphone, ele não foi instalado."
+ "Como o pacote parece ser inválido, o app não foi instalado."
+ "Não foi possível instalar %1$s em seu tablet."
+ "Não foi possível instalar %1$s na sua TV."
+ "Não foi possível instalar %1$s em seu telefone."
+ "Abrir"
+ "Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"
+ "Apps desconhecidos não podem ser instalados por este usuário"
+ "Este usuário não tem permissão para instalar apps"
+ "OK"
+ "Gerenciar apps"
+ "Sem espaço"
+ "Não foi possível instalar %1$s. Libere um pouco de espaço e tente novamente."
+ "App não encontrado"
+ "O app não foi encontrado na lista de apps instalados."
+ "Não permitido"
+ "O usuário atual não tem permissão para executar essa desinstalação."
+ "Erro"
+ "Não foi possível desinstalar o app."
+ "Desinstalar app"
+ "Desinstalar atualização"
+ "%1$s é parte do seguinte app:"
+ "Quer desinstalar este app?"
+ "Quer desinstalar este app para ""todos"" os usuários? O app e seus dados serão removidos para ""todos"" os usuários do dispositivo."
+ "Quer desinstalar este app para o usuário %1$s?"
+ "Substituir este app pela versão de fábrica? Todos os dados serão removidos."
+ "Substituir este app pela versão de fábrica? Todos os dados serão removidos. Isso afeta todos os usuários deste dispositivo, incluindo aqueles com perfis de trabalho."
+ "Executando desinstalações"
+ "Falha nas desinstalações"
+ "Desinstalando..."
+ "Desinstalando %1$s…"
+ "Desinstalação concluída."
+ "%1$s desinstalado"
+ "Desinstalação malsucedida."
+ "Falha na desinstalação de %1$s."
+ "Não é possível desinstalar o app para administrador ativo do dispositivo"
+ "Não é possível desinstalar o app para administrador ativo do dispositivo de %1$s"
+ "O app é necessário para alguns usuários ou perfis e foi desinstalado para outros"
+ "Este app é necessário para seu perfil e não pode ser desinstalado."
+ "O app é exigido pelo administrador do dispositivo e não pode ser desinstalado."
+ "Gerenciar apps do administrador do dispositivo"
+ "Gerenciar usuários"
+ "Não foi possível desinstalar %1$s."
+ "Ocorreu um problema ao analisar o pacote."
+ "Novas"
+ "Todas"
+ "Privacidade"
+ "Acesso ao dispositivo"
+ "Esta atualização não requer novas permissões."
+ "Negar"
+ "Mais informações"
+ "Negar mesmo assim"
+ "%1$s de %2$s"
+ "Permitir que o app <b>%1$s</b> %2$s?"
+ "Sempre permitir que <b>%1$s</b> %2$s?"
+ "Apenas ao usar o app"
+ "Sempre"
+ "Negar e não perguntar novamente"
+ "%1$d desativada(s)"
+ "todas desativadas"
+ "nenhuma desativada"
+ "Permitir"
+ "Apps"
+ "Permissões do app"
+ "Não perguntar novamente"
+ "Sem permissões"
+ "Outras permissões"
+ "Abrir informações do app"
+
+ Mais %1$d
+ Mais %1$d
+
+ "Este app foi projetado para uma versão anterior do Android. Negar a permissão pode fazer com que ele deixe de funcionar conforme esperado."
+ "executar uma ação desconhecida"
+ "%1$d de %2$d apps permitidos"
+ "Mostrar sistema"
+ "Ocultar sistema"
+ "Nenhum app"
+ "Configurações de localização"
+ "%1$s é um provedor de serviços de localização para este dispositivo. O acesso local pode ser modificado nas configurações de localização."
+ "Se você negar essa permissão, recursos básicos do seu dispositivo poderão não funcionar mais como deveriam."
+ "Aplicável por política"
+ "Acesso em segundo plano desativado pela política"
+ "Acesso em segundo plano ativado pela política"
+ "Acesso em primeiro plano ativado pela política"
+ "Controlada pelo administrador"
+
+
+
+ "Sempre"
+ "Apenas ao usar o app"
+ "Nunca"
+ "Carregando…"
+ "Todas as permissões"
+ "Outros recursos do app"
+ "Solicitação de permissão"
+ "Sobreposição de tela detectada"
+ "Para alterar a configuração dessa permissão, você deve primeiro desativar a sobreposição de tela em \"Config.\" > \"Apps\""
+ "Abrir configurações"
+ "Android Wear"
+ "As ações de instalar/desinstalar não são compatíveis com o Android Wear."
+ "Escolha o que o app <b>%1$s</b> terá permissão de acessar"
+ "O app <b>%1$s</b> foi atualizado. Escolha o que esse app terá permissão de acessar."
+ "Cancelar"
+ "Continuar"
+ "Novas permissões"
+ "Permissões atuais"
+ "Promovendo app…"
+ "Desconhecido"
+ "Para sua segurança, seu tablet não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Para sua segurança, sua TV não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Para sua segurança, seu smartphone não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Seu smartphone e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu smartphone ou pela perda de dados que possa resultar do uso desse app."
+ "Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu tablet ou pela perda de dados que possa resultar do uso desse app."
+ "Sua TV e seus dados pessoais estão mais vulneráveis a ataques por apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano à sua TV ou pela perda de dados que possa resultar do uso dese app."
+ "Continuar"
+ "Configurações"
+ "Instalando/desinstalando apps do Wear"
+
diff --git a/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml b/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml
new file mode 100644
index 0000000000000..206676b61d911
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Recusar e não perguntar novamente"
+ "Pode alterar esta definição mais tarde em Definições > Aplicações"
+ "%1$s/%2$s"
+ "Mostrar aplicações do sistema"
+ "Autorizações da aplicação"
+ "Autorizações da aplicação"
+ "Autorizações de %1$s"
+ "Autorizações adicionais"
+ "Autorizações de %1$s"
+
diff --git a/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml b/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml
new file mode 100644
index 0000000000000..e3c9e9f345893
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Recusar e não perguntar nov."
+ "%1$s/%2$s"
+ "Mostrar aplicações do sistema"
+ "Não pode ser alterado"
+ "Sim"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-pt-rPT/strings.xml b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
new file mode 100644
index 0000000000000..c3808397e50b2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-rPT/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Programa de instalação do pacote"
+ "Seguinte"
+ "Instalar"
+ "Concluído"
+ "Cancelar"
+ "A instalar..."
+ "A instalar %1$s…"
+ "Aplicação instalada."
+ "Pretende instalar esta aplicação? Terá acesso a:"
+ "Pretende instalar esta aplicação? Não requer qualquer acesso especial."
+ "Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. A aplicação atualizada terá acesso a:"
+ "Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. A aplicação atualizada terá acesso a:"
+ "Pretende instalar uma atualização para a aplicação existente? Os dados existentes não serão perdidos. Não é necessário um acesso específico."
+ "Pretende instalar uma atualização para a aplicação integrada? Os dados existentes não serão perdidos. Não é necessário um acesso específico."
+ "Aplicação não instalada."
+ "Foi bloqueada a instalação do pacote."
+ "A aplicação não foi instalada porque o pacote entra em conflito com um pacote existente."
+ "A aplicação não foi instalada porque não é compatível com o seu tablet."
+ "Esta aplicação não é compatível com a sua TV."
+ "A aplicação não foi instalada porque não é compatível com o seu telemóvel."
+ "A aplicação não foi instalada porque o pacote parece ser inválido."
+ "Não foi possível instalar %1$s no tablet."
+ "Não foi possível instalar o %1$s na sua TV."
+ "Não foi possível instalar %1$s no telemóvel."
+ "Abrir"
+ "O gestor não permite a instalação de aplicações obtidas de fontes desconhecidas"
+ "Este utilizador não pode instalar aplicações desconhecidas"
+ "Este utilizador não tem permissão para instalar aplicações"
+ "OK"
+ "Gerir aplicações"
+ "Sem espaço"
+ "Não foi possível instalar %1$s. Liberte algum espaço e tente novamente."
+ "Aplicação não encontrada"
+ "A aplicação não foi encontrada na lista de aplicações instaladas."
+ "Não autorizado"
+ "O utilizador atual não tem autorização para efetuar esta desinstalação."
+ "Erro"
+ "Não foi possível desinstalar a aplicação."
+ "Desinstalar a aplicação"
+ "Desinstalar atualização"
+ "%1$s faz parte da seguinte aplicação:"
+ "Pretende desinstalar esta aplicação?"
+ "Pretende desinstalar esta aplicação para ""todos"" os utilizadores? A aplicação e os respetivos dados serão removidos de ""todos"" os utilizadores do dispositivo."
+ "Pretende desinstalar esta aplicação para o utilizador %1$s?"
+ "Pretende substituir esta aplicação pela versão de fábrica? Todos os dados são removidos."
+ "Pretende substituir esta aplicação pela versão de fábrica? Todos os dados são removidos. Esta ação afeta todos os utilizadores deste dispositivo, incluindo os que têm perfis de trabalho."
+ "Desinstalações em execução"
+ "Desinstalações com falha"
+ "A desinstalar..."
+ "A desinstalar a aplicação %1$s…"
+ "Desinstalação concluída."
+ "A aplicação %1$s foi desinstalada"
+ "Desinstalação sem êxito."
+ "Falha ao desinstalar a aplicação %1$s."
+ "Não é possível desinstalar a aplicação de administração de dispositivos ativa"
+ "Não é possível desinstalar a aplicação de administração de dispositivos ativa para %1$s"
+ "Esta aplicação é necessária para alguns utilizadores ou perfis e foi desinstalada para outros"
+ "O perfil necessita desta aplicação e não é possível desinstalá-la."
+ "Esta aplic. é exigida pelo gestor do disp. e não pode ser desinstalada."
+ "Gerir aplicações de administração de dispositivos"
+ "Gerir utilizadores"
+ "Não foi possível desinstalar %1$s."
+ "Ocorreu um problema ao analisar o pacote."
+ "Novas"
+ "Todas"
+ "Privacidade"
+ "Acesso ao Dispositivo"
+ "Esta atualização não requer novas permissões."
+ "Recusar"
+ "Mais informações"
+ "Recusar mesmo assim"
+ "%1$s de %2$s"
+ "Pretende permitir à(ao) <b>%1$s</b> %2$s?"
+ "Pretende permitir que a aplicação <b>%1$s</b> %2$s sempre?"
+ "Apenas ao utilizar a aplicação"
+ "Sempre"
+ "Recusar e não perguntar novamente"
+ "%1$d desativadas"
+ "todas desativadas"
+ "nenhuma desativada"
+ "Permitir"
+ "Aplicações"
+ "Permissões da aplicação"
+ "Não perguntar novamente"
+ "Sem autorizações"
+ "Autorizações adicionais"
+ "Abrir informações da aplicação"
+
+ Mais %1$d
+ Mais %1$d
+
+ "Esta aplicação foi concebida para uma versão mais antiga do Android. Negar autorização pode fazer com que deixe de funcionar como pretendido."
+ "executar uma ação desconhecida"
+ "%1$d de %2$d aplicações autorizadas"
+ "Mostrar sistema"
+ "Ocultar sistema"
+ "Sem aplicações"
+ "Definições de localização"
+ "O %1$s é um fornecedor de serviços de localização para este dispositivo. É possível modificar o acesso à localização a partir das definições de localização."
+ "Se negar esta autorização, as funcionalidades básicas do seu dispositivo podem deixar de funcionar corretamente."
+ "Imposta pela política"
+ "Acesso em segundo plano desativado pela política"
+ "Acesso em segundo plano ativado pela política"
+ "Acesso em primeiro plano ativado pela política"
+ "Controlado pelo administrador"
+
+
+
+ "Sempre"
+ "Apenas ao utilizar a aplicação"
+ "Nunca"
+ "A carregar…"
+ "Todas as autorizações"
+ "Outras capacidades de aplicações"
+ "Pedido de autorização"
+ "Sobreposição de ecrã detetada"
+ "Para alterar esta definição de autorização, primeiro tem de desativar a sobreposição do ecrã em Definições > Aplicações"
+ "Abrir definições"
+ "Android Wear"
+ "As ações de instalar/desinstalar não são compatíveis com o Android Wear."
+ "Escolher a que conteúdos permite que o <b>%1$s</b> aceda"
+ "O <b>%1$s</b> foi atualizado. Escolha a que conteúdos permite que esta aplicação aceda."
+ "Cancelar"
+ "Continuar"
+ "Novas autorizações"
+ "Autorizações atuais"
+ "A preparar a aplicação…"
+ "Desconhecido"
+ "Para sua segurança, o tablet não está autorizado a instalar aplicações desconhecidas a partir desta fonte."
+ "Para sua segurança, a TV não está autorizada a instalar aplicações desconhecidas a partir desta fonte."
+ "Para sua segurança, o telemóvel não está autorizado a instalar aplicações desconhecidas a partir desta fonte."
+ "O seu telemóvel e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados ao telemóvel ou pelas perdas de dados que possam resultar da utilização da mesma."
+ "O seu tablet e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados ao tablet ou pelas perdas de dados que possam resultar da utilização da mesma."
+ "A sua TV e os dados pessoais estão mais vulneráveis a ataques por parte de aplicações desconhecidas. Ao instalar esta aplicação, concorda que é responsável por quaisquer danos causados à TV ou pelas perdas de dados que possam resultar da utilização da mesma."
+ "Continuar"
+ "Definições"
+ "Instalar/desinstalar aplicações Wear"
+
diff --git a/packages/PackageInstaller/res/values-pt-television/strings.xml b/packages/PackageInstaller/res/values-pt-television/strings.xml
new file mode 100644
index 0000000000000..ba6e68f3d933d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Negar e não perguntar novamente"
+ "É possível alterar isso mais tarde em \"Config.\" > \"Apps\""
+ "%1$s / %2$s"
+ "Mostrar apps do sistema"
+ "Permissões do app"
+ "Permissões do app"
+ "Permissões para %1$s"
+ "Outras permissões"
+ "Permissões para %1$s"
+
diff --git a/packages/PackageInstaller/res/values-pt-watch/strings.xml b/packages/PackageInstaller/res/values-pt-watch/strings.xml
new file mode 100644
index 0000000000000..8742c2db4500c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Negar e não perguntar de novo"
+ "%1$s / %2$s"
+ "Mostrar apps do sistema"
+ "Impossível alterar"
+ "Sim"
+ "Cancelar"
+
diff --git a/packages/PackageInstaller/res/values-pt/strings.xml b/packages/PackageInstaller/res/values-pt/strings.xml
new file mode 100644
index 0000000000000..feb2337bea96a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-pt/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instalador do pacote"
+ "Próximo"
+ "Instalar"
+ "Concluído"
+ "Cancelar"
+ "Instalando..."
+ "Instalando %1$s…"
+ "App instalado."
+ "Quer instalar este app? Ele terá acesso a:"
+ "Quer instalar este app? Não requer acesso especial."
+ "Quer instalar uma atualização para este app? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"
+ "Quer instalar uma atualização para este app integrado? Os dados existentes não serão perdidos. O app atualizado terá acesso a:"
+ "Quer instalar uma atualização para este app existente? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."
+ "Quer instalar uma atualização para este app integrado? Seus dados existentes não serão perdidos. A atualização não requer qualquer acesso especial."
+ "O app não foi instalado."
+ "A instalação do pacote foi bloqueada."
+ "Como o pacote tem um conflito com um pacote já existente, o app não foi instalado."
+ "Como o app não é compatível com seu tablet, ele não foi instalado."
+ "Este app não é compatível com sua TV."
+ "Como o app não é compatível com seu smartphone, ele não foi instalado."
+ "Como o pacote parece ser inválido, o app não foi instalado."
+ "Não foi possível instalar %1$s em seu tablet."
+ "Não foi possível instalar %1$s na sua TV."
+ "Não foi possível instalar %1$s em seu telefone."
+ "Abrir"
+ "Seu administrador não permite a instalação de apps transferidos por download de fontes desconhecidas"
+ "Apps desconhecidos não podem ser instalados por este usuário"
+ "Este usuário não tem permissão para instalar apps"
+ "OK"
+ "Gerenciar apps"
+ "Sem espaço"
+ "Não foi possível instalar %1$s. Libere um pouco de espaço e tente novamente."
+ "App não encontrado"
+ "O app não foi encontrado na lista de apps instalados."
+ "Não permitido"
+ "O usuário atual não tem permissão para executar essa desinstalação."
+ "Erro"
+ "Não foi possível desinstalar o app."
+ "Desinstalar app"
+ "Desinstalar atualização"
+ "%1$s é parte do seguinte app:"
+ "Quer desinstalar este app?"
+ "Quer desinstalar este app para ""todos"" os usuários? O app e seus dados serão removidos para ""todos"" os usuários do dispositivo."
+ "Quer desinstalar este app para o usuário %1$s?"
+ "Substituir este app pela versão de fábrica? Todos os dados serão removidos."
+ "Substituir este app pela versão de fábrica? Todos os dados serão removidos. Isso afeta todos os usuários deste dispositivo, incluindo aqueles com perfis de trabalho."
+ "Executando desinstalações"
+ "Falha nas desinstalações"
+ "Desinstalando..."
+ "Desinstalando %1$s…"
+ "Desinstalação concluída."
+ "%1$s desinstalado"
+ "Desinstalação malsucedida."
+ "Falha na desinstalação de %1$s."
+ "Não é possível desinstalar o app para administrador ativo do dispositivo"
+ "Não é possível desinstalar o app para administrador ativo do dispositivo de %1$s"
+ "O app é necessário para alguns usuários ou perfis e foi desinstalado para outros"
+ "Este app é necessário para seu perfil e não pode ser desinstalado."
+ "O app é exigido pelo administrador do dispositivo e não pode ser desinstalado."
+ "Gerenciar apps do administrador do dispositivo"
+ "Gerenciar usuários"
+ "Não foi possível desinstalar %1$s."
+ "Ocorreu um problema ao analisar o pacote."
+ "Novas"
+ "Todas"
+ "Privacidade"
+ "Acesso ao dispositivo"
+ "Esta atualização não requer novas permissões."
+ "Negar"
+ "Mais informações"
+ "Negar mesmo assim"
+ "%1$s de %2$s"
+ "Permitir que o app <b>%1$s</b> %2$s?"
+ "Sempre permitir que <b>%1$s</b> %2$s?"
+ "Apenas ao usar o app"
+ "Sempre"
+ "Negar e não perguntar novamente"
+ "%1$d desativada(s)"
+ "todas desativadas"
+ "nenhuma desativada"
+ "Permitir"
+ "Apps"
+ "Permissões do app"
+ "Não perguntar novamente"
+ "Sem permissões"
+ "Outras permissões"
+ "Abrir informações do app"
+
+ Mais %1$d
+ Mais %1$d
+
+ "Este app foi projetado para uma versão anterior do Android. Negar a permissão pode fazer com que ele deixe de funcionar conforme esperado."
+ "executar uma ação desconhecida"
+ "%1$d de %2$d apps permitidos"
+ "Mostrar sistema"
+ "Ocultar sistema"
+ "Nenhum app"
+ "Configurações de localização"
+ "%1$s é um provedor de serviços de localização para este dispositivo. O acesso local pode ser modificado nas configurações de localização."
+ "Se você negar essa permissão, recursos básicos do seu dispositivo poderão não funcionar mais como deveriam."
+ "Aplicável por política"
+ "Acesso em segundo plano desativado pela política"
+ "Acesso em segundo plano ativado pela política"
+ "Acesso em primeiro plano ativado pela política"
+ "Controlada pelo administrador"
+
+
+
+ "Sempre"
+ "Apenas ao usar o app"
+ "Nunca"
+ "Carregando…"
+ "Todas as permissões"
+ "Outros recursos do app"
+ "Solicitação de permissão"
+ "Sobreposição de tela detectada"
+ "Para alterar a configuração dessa permissão, você deve primeiro desativar a sobreposição de tela em \"Config.\" > \"Apps\""
+ "Abrir configurações"
+ "Android Wear"
+ "As ações de instalar/desinstalar não são compatíveis com o Android Wear."
+ "Escolha o que o app <b>%1$s</b> terá permissão de acessar"
+ "O app <b>%1$s</b> foi atualizado. Escolha o que esse app terá permissão de acessar."
+ "Cancelar"
+ "Continuar"
+ "Novas permissões"
+ "Permissões atuais"
+ "Promovendo app…"
+ "Desconhecido"
+ "Para sua segurança, seu tablet não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Para sua segurança, sua TV não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Para sua segurança, seu smartphone não tem permissão para instalar apps desconhecidos dessa fonte."
+ "Seu smartphone e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu smartphone ou pela perda de dados que possa resultar do uso desse app."
+ "Seu tablet e seus dados pessoais estão mais vulneráveis a ataques de apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano causado ao seu tablet ou pela perda de dados que possa resultar do uso desse app."
+ "Sua TV e seus dados pessoais estão mais vulneráveis a ataques por apps desconhecidos. Ao instalar esse app, você concorda que é responsável por qualquer dano à sua TV ou pela perda de dados que possa resultar do uso dese app."
+ "Continuar"
+ "Configurações"
+ "Instalando/desinstalando apps do Wear"
+
diff --git a/packages/PackageInstaller/res/values-ro-television/strings.xml b/packages/PackageInstaller/res/values-ro-television/strings.xml
new file mode 100644
index 0000000000000..4f8992f3d54f8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Respingeți și nu se mai întreabă"
+ "Puteți modifica permisiunile ulterior din Setări > Aplicații"
+ "%1$s/%2$s"
+ "Afișează aplicațiile de sistem"
+ "Permisiuni aplicații"
+ "Permisiuni aplicații"
+ "Permisiuni pentru %1$s"
+ "Permisiuni suplimentare"
+ "Permisiuni pentru %1$s"
+
diff --git a/packages/PackageInstaller/res/values-ro-watch/strings.xml b/packages/PackageInstaller/res/values-ro-watch/strings.xml
new file mode 100644
index 0000000000000..ea699f32da813
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Respingeți; nu se mai întreabă"
+ "%1$s/%2$s"
+ "Afișează aplicațiile de sistem"
+ "Nu se poate modifica"
+ "Da"
+ "Anulați"
+
diff --git a/packages/PackageInstaller/res/values-ro/strings.xml b/packages/PackageInstaller/res/values-ro/strings.xml
new file mode 100644
index 0000000000000..3b13dab3d7e08
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ro/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Program de instalare a pachetelor"
+ "Înainte"
+ "Instalați"
+ "Terminat"
+ "Anulați"
+ "În curs de instalare..."
+ "Se instalează %1$s…"
+ "Aplicație instalată."
+ "Doriți să instalați această aplicație? Aceasta va avea acces la:"
+ "Doriți să instalați această aplicație? Aplicația nu solicită un acces special."
+ "Doriți să instalați o actualizare pentru această aplicație existentă? Datele existente nu vor fi pierdute. Aplicația actualizată va avea acces la:"
+ "Doriți să instalați o actualizare pentru această aplicație încorporată? Datele existente nu vor fi pierdute. Aplicația actualizată va avea acces la:"
+ "Doriți să instalați o actualizare pentru această aplicație existentă? Datele existente nu vor fi pierdute. Actualizarea nu are nevoie de acces special."
+ "Doriți să instalați o actualizare pentru această aplicație încorporată? Datele existente nu vor fi pierdute. Actualizarea nu are nevoie de acces special."
+ "Aplicația nu este instalată."
+ "Instalarea pachetului a fost blocată."
+ "Aplicația nu a fost instalată deoarece pachetul intră în conflict cu un pachet existent."
+ "Aplicația nu a fost instalată deoarece nu este compatibilă cu tableta dvs."
+ "Această aplicație nu este compatibilă cu televizorul dvs."
+ "Aplicația nu a fost instalată deoarece nu este compatibilă cu telefonul dvs."
+ "Aplicația nu a fost instalată deoarece se pare că pachetul este nevalid."
+ "%1$s nu a putut fi instalată pe tableta dvs."
+ "Aplicația %1$s nu a putut fi instalată pe televizor."
+ "%1$s nu a putut fi instalată pe telefonul dvs."
+ "Deschideți"
+ "Administratorul nu permite instalarea aplicațiilor obținute din surse necunoscute"
+ "Aplicațiile necunoscute nu pot fi instalate de acest utilizator"
+ "Acest utilizator nu are permisiunea să instaleze aplicații"
+ "OK"
+ "Gestionați aplicații"
+ "Spațiu de stocare insuficient"
+ "%1$s nu a putut fi instalată. Eliberați spațiu și încercați din nou."
+ "Aplicația nu a fost găsită"
+ "Aplicația nu a fost găsită în lista de aplicații instalate."
+ "Nu are permisiune"
+ "Utilizatorul actual nu are permisiune pentru a face această dezinstalare."
+ "Eroare"
+ "Aplicația nu a putut fi dezinstalată."
+ "Dezinstalați aplicația"
+ "Dezinstalați actualizarea"
+ " %1$s face parte din următoarea aplicație:"
+ "Doriți să dezinstalați această aplicație?"
+ "Doriți să dezinstalați această aplicație pentru ""toți"" utilizatorii? Aplicația și datele acesteia vor fi eliminate de la ""toți"" utilizatorii de pe acest dispozitiv."
+ "Dezinstalați această aplicație pentru utilizatorul %1$s?"
+ "Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate."
+ "Înlocuiți această aplicație cu versiunea din fabrică? Toate datele vor fi eliminate. Această acțiune va afecta toți utilizatorii dispozitivului, inclusiv pe cei cu profiluri de serviciu."
+ "Dezinstalări în curs"
+ "Dezinstalări nereușite"
+ "În curs de dezinstalare..."
+ "Se dezinstalează %1$s…"
+ "Dezinstalare finalizată."
+ "%1$s a fost dezinstalat"
+ "Dezinstalare nefinalizată."
+ "%1$s nu a putut fi dezinstalată."
+ "Nu se poate dezinstala aplicația activă de administrare a dispozitivului"
+ "Nu se poate dezinstala aplicația activă de administrare a dispozitivului pentru %1$s"
+ "Aplicația este necesară unor utilizatori sau profiluri și a fost dezinstalată pentru alții"
+ "Aplicația este necesară pentru profilul dvs. și nu poate fi dezinstalată."
+ "Aplicație necesară administratorului dispozitivului. Nu poate fi dezinstalată."
+ "Gestionați aplicațiile de administrare dispozitiv"
+ "Gestionați utilizatorii"
+ "%1$s nu a putut fi dezinstalată."
+ "A apărut o problemă la analizarea pachetului."
+ "Noi"
+ "Toate"
+ "Confidențialitate"
+ "Acces la dispozitiv"
+ "Această actualizare nu necesită permisiuni noi."
+ "Refuzați"
+ "Mai multe informații"
+ "Nu permiteți oricum"
+ "%1$s din %2$s"
+ "Permiteți <b>%1$s</b> să %2$s?"
+ "Permiteți întotdeauna <b>%1$s</b> să %2$s?"
+ "Doar în timp ce folosiți aplicația"
+ "Întotdeauna"
+ "Respingeți și nu se mai întreabă"
+ "%1$d dezactivate"
+ "toate dezactivate"
+ "niciuna dezactivată"
+ "Permiteți"
+ "Aplicații"
+ "Permisiuni pentru aplicație"
+ "Nu mai întreba"
+ "Fără permisiuni"
+ "Permisiuni suplimentare"
+ "Deschideți informațiile despre aplicații"
+
+ Încă %1$d
+ Încă %1$d
+ Încă %1$d
+
+ "Această aplicație a fost creată pentru o versiune Android mai veche. Dacă nu acordați permisiunea, este posibil ca aceasta să nu mai funcționeze corespunzător."
+ "efectuează o acțiune necunoscută"
+ "%1$d din %2$d aplicații au această permisiune"
+ "Afișați aplicațiile de sistem"
+ "Ascundeți aplicațiile de sistem"
+ "Nicio aplicație"
+ "Setări privind locația"
+ "%1$s este un furnizor de servicii de localizare pentru acest dispozitiv. Accesul la locație poate fi modificat din setările privind locația."
+ "Dacă refuzați această permisiune, este posibil ca funcțiile de bază ale dispozitivului să nu mai funcționeze corespunzător."
+ "Aplicată conform politicii"
+ "Acces la fundal dezactivat de politică"
+ "Acces la fundal activat de politică"
+ "Acces la prim-plan activat de politică"
+ "Controlat de administrator"
+
+
+
+ "Întotdeauna"
+ "Doar în timp ce folosiți aplicația"
+ "Niciodată"
+ "Se încarcă..."
+ "Toate permisiunile"
+ "Alte funcții ale aplicației"
+ "Solicitare de permisiune"
+ "S-a detectat suprapunerea pe ecran"
+ "Ca să schimbați această setare pentru permisiuni, mai întâi trebuie să dezactivați suprapunerea pe ecran din Setări > Aplicații"
+ "Deschideți setările"
+ "Android Wear"
+ "Acțiunile Instalați/Dezinstalați nu sunt acceptate pe Wear."
+ "Alegeți ce va putea accesa <b>%1$s</b>"
+ "Aplicația <b>%1$s</b> a fost actualizată. Alegeți ce va putea accesa această aplicație."
+ "Anulați"
+ "Continuați"
+ "Permisiuni noi"
+ "Permisiuni actuale"
+ "Se pregătește aplicația…"
+ "Necunoscut"
+ "Din motive de securitate, tableta dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."
+ "Din motive de securitate, televizorul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."
+ "Din motive de securitate, telefonul dvs. nu are permisiunea să instaleze aplicații necunoscute din această sursă."
+ "Telefonul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea telefonului sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."
+ "Tableta și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați aplicația, acceptați că sunteți singura persoană responsabilă pentru deteriorarea tabletei sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."
+ "Televizorul și datele dvs. personale sunt mai vulnerabile la un atac din partea aplicațiilor necunoscute. Dacă instalați această aplicație, acceptați că sunteți singura persoană responsabilă pentru deteriorarea televizorului sau pentru pierderea datelor, care pot avea loc în urma utilizării acesteia."
+ "Continuați"
+ "Setări"
+ "Se (dez)instalează aplicațiile Wear"
+
diff --git a/packages/PackageInstaller/res/values-round-watch/dimens.xml b/packages/PackageInstaller/res/values-round-watch/dimens.xml
new file mode 100644
index 0000000000000..fa36464430a99
--- /dev/null
+++ b/packages/PackageInstaller/res/values-round-watch/dimens.xml
@@ -0,0 +1,23 @@
+
+
+
+
+ 48dp
+ @dimen/screen_percentage_15
+ @dimen/screen_percentage_15
+ @dimen/screen_percentage_12
+ @dimen/screen_percentage_10
+
diff --git a/packages/PackageInstaller/res/values-ru-television/strings.xml b/packages/PackageInstaller/res/values-ru-television/strings.xml
new file mode 100644
index 0000000000000..e105f9242d9fb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Запретить и больше не спрашивать"
+ "Чтобы изменить разрешения, откройте \"Настройки\" и выберите \"Приложения\"."
+ "%1$s/%2$s"
+ "Показать системные приложения"
+ "Разрешения приложений"
+ "Разрешения приложений"
+ "Разрешения (%1$s)"
+ "Ещё разрешения"
+ "Разрешения (%1$s)"
+
diff --git a/packages/PackageInstaller/res/values-ru-watch/strings.xml b/packages/PackageInstaller/res/values-ru-watch/strings.xml
new file mode 100644
index 0000000000000..f58db32d87d08
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Запретить и не спрашивать"
+ "%1$s/%2$s"
+ "Показать системные приложения"
+ "Нельзя изменить"
+ "Да"
+ "Отмена"
+
diff --git a/packages/PackageInstaller/res/values-ru/strings.xml b/packages/PackageInstaller/res/values-ru/strings.xml
new file mode 100644
index 0000000000000..9ffdf4a07ecf2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ru/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Установщик пакетов"
+ "Далее"
+ "Установить"
+ "Готово"
+ "Отмена"
+ "Установка..."
+ "Устанавливаем %1$s…"
+ "Приложение установлено."
+ "Хотите ли вы установить это приложение? Оно получит следующие разрешения:"
+ "Это приложение не требует специальных разрешений. Установить его?"
+ "Хотите установить обновление для этого приложения? После обновления оно сможет выполнять следующие действия:"
+ "Хотите установить обновление для этого приложения? После обновления оно сможет выполнять следующие действия:"
+ "Установить обновление этого приложения? На текущих данных это никак не отразится. Специальных прав доступа не требуется."
+ "Установить обновление этого встроенного приложения? На текущих данных это никак не отразится. Специальных прав доступа не требуется."
+ "Приложение не установлено."
+ "Установка пакета заблокирована."
+ "Приложение не установлено, так как оно конфликтует с другим пакетом."
+ "Приложение не установлено, так как оно несовместимо с вашим планшетом."
+ "Приложение несовместимо с вашим телевизором."
+ "Приложение не установлено, так как оно несовместимо с вашим телефоном."
+ "Приложение не установлено, так как его пакет недействителен (например, поврежден)."
+ "Не удалось установить приложение \"%1$s\"."
+ "Приложение \"%1$s\" нельзя установить на ваш телевизор."
+ "Не удалось установить приложение \"%1$s\"."
+ "Открыть"
+ "Ваш администратор запретил устанавливать приложения из неизвестных источников"
+ "Этот пользователь не может устанавливать неизвестные приложения"
+ "Этому пользователю не разрешено устанавливать приложения"
+ "ОК"
+ "Управление приложениями"
+ "Недостаточно места"
+ "Не удалось установить приложение \"%1$s\". Освободите место и повторите попытку."
+ "Приложение не найдено"
+ "Приложения нет в списке установленных."
+ "Действие запрещено"
+ "Этот пользователь не может удалить приложение."
+ "Ошибка"
+ "Не удалось удалить приложение."
+ "Удаление приложения"
+ "Удаление обновления"
+ "%1$s – часть следующего приложения:"
+ "Удалить приложение?"
+ "Удалить это приложение для ""всех"" пользователей? После этого ""ни один"" пользователь устройства не будет иметь доступа к приложению и связанным с ним данным."
+ "Удалить это приложение из профиля %1$s?"
+ "Установить исходную версию приложения? Все его данные будут удалены."
+ "Установить исходную версию приложения? Его данные будут удалены из всех профилей устройства, в том числе рабочих."
+ "Активные процессы удаления"
+ "Ошибки удаления"
+ "Удаление..."
+ "Удаление приложения \"%1$s\"…"
+ "Удаление завершено."
+ "Приложение \"%1$s\" удалено"
+ "Ошибка при удалении."
+ "Не удалось удалить приложение \"%1$s\"."
+ "Невозможно удалить активное приложение для администрирования устройства"
+ "Невозможно удалить активное приложение для администрирования устройства в профиле %1$s"
+ "Это приложение обязательно для некоторых пользователей или профилей."
+ "Это приложение обязательно для вашего профиля. Его нельзя удалить."
+ "Это приложение указано администратором как обязательное. Его нельзя удалить."
+ "Настроить приложения для администрир. устройства"
+ "Управление пользователями"
+ "Не удалось удалить приложение \"%1$s\"."
+ "Ошибка при синтаксическом анализе пакета."
+ "Новые"
+ "Все"
+ "Личные данные"
+ "Доступ к устройству"
+ "Установка этого обновления не требует новых разрешений."
+ "Отклонить"
+ "Подробнее"
+ "Все равно запретить"
+ "%1$s из %2$s"
+ "Разрешить приложению <b>%1$s</b> %2$s?"
+ "Всегда разрешать приложению \"%1$s\" %2$s?"
+ "Когда открыто приложение"
+ "Всегда"
+ "Запретить и больше не спрашивать"
+ "отключено: %1$d"
+ "все отключены"
+ "все включены"
+ "Разрешить"
+ "Приложения"
+ "Разрешения приложений"
+ "Больше не спрашивать"
+ "Нет разрешений"
+ "Ещё разрешения"
+ "О приложении"
+
+ Ещё %1$d
+ Ещё %1$d
+ Ещё %1$d
+ Ещё %1$d
+
+ "Это приложение было разработано для более ранней версии Android. Отзыв разрешения может вызвать неполадки в работе."
+ "выполняет неизвестные действия"
+ "Приложений с разрешением: %1$d из %2$d"
+ "Показать системные процессы"
+ "Скрыть системные процессы"
+ "Нет приложений"
+ "Настройки геоданных"
+ "%1$s является поставщиком услуг геолокации для этого устройства. Вы можете изменить параметры доступа в настройках геоданных."
+ "Если вы отключите это разрешение, основные функции устройства могут работать неправильно."
+ "В соответствии с политикой"
+ "Доступ в фоновом режиме отключен в соответствии с правилами."
+ "Доступ в фоновом режиме включен в соответствии с правилами."
+ "Активный режим включен в соответствии с правилами."
+ "Контролируется администратором"
+
+
+
+ "Всегда"
+ "Когда открыто приложение"
+ "Никогда"
+ "Загрузка…"
+ "Все разрешения"
+ "Что ещё может приложение"
+ "Запрос разрешений"
+ "Показ поверх других окон"
+ "Чтобы предоставить или отменить разрешение, сначала отключите показ поверх других окон. Для этого нажмите \"Настройки > Приложения\"."
+ "Открыть настройки"
+ "Android Wear"
+ "Установка и удаление не поддерживаются на Android Wear"
+ "Выберите разрешения для приложения <b>%1$s</b>"
+ "Приложение <b>%1$s</b> обновлено. Выберите разрешения для него."
+ "Отмена"
+ "Далее"
+ "Новые разрешения"
+ "Имеющиеся разрешения"
+ "Подождите…"
+ "Неизвестное приложение"
+ "В целях безопасности ваш планшет блокирует установку приложений из неизвестных источников."
+ "В целях безопасности ваш телевизор блокирует установку приложений из неизвестных источников."
+ "В целях безопасности ваш телефон блокирует установку приложений из неизвестных источников."
+ "Ваши личные данные и данные телефона более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный телефону, и потерю данных, связанные с использованием этого приложения."
+ "Ваши личные данные и данные планшета более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный планшету, и потерю данных, связанные с использованием этого приложения."
+ "Ваши личные данные и данные телевизора более уязвимы для атак приложений из неизвестных источников. Устанавливая это приложение, вы соглашаетесь с тем, что несете полную ответственность за любой ущерб, нанесенный телевизору, и потерю данных, связанные с использованием этого приложения."
+ "Продолжить"
+ "Настройки"
+ "Установка/удаление приложений для Android Wear"
+
diff --git a/packages/PackageInstaller/res/values-si-television/strings.xml b/packages/PackageInstaller/res/values-si-television/strings.xml
new file mode 100644
index 0000000000000..2ac8d8b3650a3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ප්රතික්ෂේප කරන්න, නැවත අසන්න එපා"
+ "ඔබට මෙය පසුව සැකසීම් > යෙදුම් තුළ වෙනස් කළ හැකිය"
+ "%1$s / %2$s"
+ "පද්ධති යෙදුම් පෙන්වන්න"
+ "යෙදුම් අවසර"
+ "යෙදුම් අවසර"
+ "%1$s අවසර"
+ "අතිරේක අවසර"
+ "%1$s අවසර"
+
diff --git a/packages/PackageInstaller/res/values-si-watch/strings.xml b/packages/PackageInstaller/res/values-si-watch/strings.xml
new file mode 100644
index 0000000000000..c5d9ae41427b8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ප්රතික්ෂේප කරන්න, නැවත අසන්න එපා"
+ "%1$s / %2$s"
+ "පද්ධති යෙදුම් පෙන්වන්න"
+ "වෙනස් කළ නොහැකිය"
+ "ඔව්"
+ "අවලංගු කර."
+
diff --git a/packages/PackageInstaller/res/values-si/strings.xml b/packages/PackageInstaller/res/values-si/strings.xml
new file mode 100644
index 0000000000000..18fc840ea2fa5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-si/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "පැකේජ ස්ථාපනකරු"
+ "මීලඟ"
+ "ස්ථාපනය"
+ "හරි"
+ "අවලංගු කරන්න"
+ "ස්ථාපනය කරමින්…"
+ "%1$s ස්ථාපනය කරමින්…"
+ "යෙදුම ස්ථාපනය කරන ලදි."
+ "ඔබට මෙම යෙදුම ස්ථාපනය කිරීමට අවශ්යද? පහත ඒවා වෙත එයට ප්රවේශය ලැබෙනු ඇත:"
+ "මෙම යෙදුම ස්ථාපනය කිරීමට ඔබට අවශ්යද? වෙනත් විශේෂ ප්රවේශයක් එයට අවශ්ය නොවෙයි."
+ "දැනට පවතින මෙම යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්යද? ඔබගේ පවතින දත්ත නැති නොවේ. යාවත්කාලීන කළ යෙදුම පිවිසීම ලබා ගනී:"
+ "ඔබට මෙම තිළැලි යෙදුමට යාවත්කාලීනය ස්ථාපනය කිරීමට අවශ්යද? ඔබගේ දැනට පවතින දත්ත නැති නොවේ. යාවත්කාලීන කරන ලද යෙදුම පහත සඳහා ප්රවේශය ලබාගනු ඇත:"
+ "මෙම පවතින යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්යද? ඔබගේ පවතින දත්ත නැති නොවේ. එයට විශේෂ ප්රවේශයක් අවශ්ය නොවේ."
+ "පවතින මෙම යෙදුමට යාවත්කාලීනයක් ස්ථාපනය කිරීමට ඔබට අවශ්යද? ඔබගේ පවතින දත්ත නැති නොවේ. එයට විශේෂ ප්රවේශයක් අවශ්ය නොවේ."
+ "යෙදුම ස්ථාපනය කරේ නැත."
+ "මෙම පැකේජය ස්ථාපනය කිරීම අවහිර කරන ලදි."
+ "පැකේජය දැනට පවතින පැකේජයක් සමග ගැටෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."
+ "යෙදුම ඔබේ ටැබ්ලට් පරිගණකය සමග නොගැළපෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."
+ "මෙම යෙදුම ඔබගේ රූපවාහිනිය හා නොගැළපේ."
+ "යෙදුම ඔබේ දුරකථනය සමග නොගැළපෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."
+ "පැකේජය වලංගු නොවන බවක් පෙනෙන නිසා යෙදුම ස්ථාපනය නොකරන ලදී."
+ "ඔබගේ ටැබ්ලටයේ %1$s ස්ථාපනය කළ නොහැක."
+ "%1$s මෙම රූපවාහිනියෙහි ස්ථාපනය කළ නොහැක."
+ "%1$s දුරකථනයට ස්ථාපිත කිරීමට නොහැකි විය."
+ "විවෘත කරන්න"
+ "නාඳුනන මූලයන් වෙතින් ලබාගත් යෙදුම් ස්ථාපනය කිරීමට ඔබගේ පරිපාලකයා ඉඩ නොදේ"
+ "මෙම පරිශීලකයා මඟින් නොදන්නා යෙදුම් ස්ථාපනය කළ නොහැක"
+ "මෙම පරිශීලකයාට යෙදුම් ස්ථාපනය කිරීමට අවසර නැත"
+ "හරි"
+ "යෙදුම් කළමනාකරණය කරන්න"
+ "ඉඩ නොමැත"
+ "%1$s ස්ථාපිත කිරීමට නොහැකි විය. ඉඩ පොඩ්ඩක් නිදහස් කොට නැවත උත්සාහ කරන්න."
+ "යෙදුම හමුවී නැත"
+ "ස්ථාපිත යෙදුම් ලැයිස්තුවේ යෙදුම සොයා ගත නොහැකි විය."
+ "ඉඩ නොදෙයි"
+ "වත්මන් පරිශීලකයාට මෙම අස්ථාපනය සිදු කිරීමට ඉඩ නොදේ."
+ "දෝෂය"
+ "යෙදුම අස්ථාපනය කළ නොහැකි විය."
+ "යෙදුම අස්ථාපනය කරන්න"
+ "යාවත්කාලිනය අස්ථාපනය කරන්න"
+ "%1$s පහත යෙදුමේ කොටසකි:"
+ "ඔබට මෙම යෙදුම අස්ථාපනය කිරීමට අවශ්යද?"
+ "සියලු"" පරිශීලකයන් සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්යද? උපාංගයෙහි ""සියලු"" පරිශීලකයන් සඳහා යෙදුම සහ එහි දත්ත ඉවත්වනු ඇත."
+ "%1$s පරිශීලකයා සඳහා මෙම යෙදුම අස්ථාපනය කිරීමට ඔබට අවශ්යයද?"
+ "මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත."
+ "මෙම යෙදුම කර්මාන්ත ශාලා අනුවාදයක් සමගින් ප්රතිස්ථාපනය කරන්නද? සියලු දත්ත ඉවත් කරනු ඇත. මෙය කාර්යාල පැතිකඩවල් සහිත අය ඇතුළුව, මෙම උපාංගයෙහි සියලු පරිශීලකයන් වෙත බලපානු ඇත."
+ "අස්ථාපන ධාවනය කරමින්"
+ "අසාර්ථක වූ අස්ථාපන"
+ "අස්ථාපනය කරමින්…"
+ "%1$s අස්ථාපනය කරමින්…"
+ "අස්ථාපනය අවසන්."
+ "%1$s අස්ථාපනය කරන ලදී"
+ "අස්ථාපිත විම අසාර්ථකයි."
+ "%1$s අස්ථාපනය කිරීම සාර්ථකයි."
+ "ක්රියාකාරී උපාංගය පරිපාලක යෙදුම අස්ථාපනය කිරීමට නොහැක"
+ "%1$s සඳහා ක්රියාකාරී උපාංගය පරිපාලක යෙදුම අස්ථාපනය කිරීමට නොහැක"
+ "මෙම යෙදුම සමහර පරිශීලකයන්ට සහ පැතිකඩවල්වලට අවශ්ය අතර අනෙක් අයට අස්ථාපනය කරන ලදී"
+ "ඔබේ කාර්ය පැතිකඩ සඳහා මෙම යෙදුම අවශ්ය වන අතර අස්ථාපනය කළ නොහැකිය."
+ "ඔබගේ උපාංගයේ පාලකයාට මෙම යෙදුම අවශ්ය වේ එම නිසා අස්ථාපනය කළ නොහැක."
+ "උපාංග පරිපාලක යෙදුම් කළමනාකරණය කිරීම"
+ "පරිශීලකයන් කළමනාකරණය කරන්න"
+ "%1$s අස්ථාපනය කල නොහැක."
+ "පැකේජය විග්රහ කිරීමේදී ගැටළුවක් ඇති විය."
+ "අලුත්"
+ "සියල්ල"
+ "පෞද්ගලිකත්වය"
+ "උපාංගය ප්රවේශය"
+ "මෙම යාවත්කාලිනයට අලුත් අවසරයන් අවශ්ය නොවේ."
+ "ප්රතික්ෂේප කරන්න"
+ "වැඩිදුර තොරතුරු"
+ "කෙසේ වෙතත් ප්රතික්ෂේප කරන්න"
+ "%2$s න් %1$s"
+ "<b>%1$s</b> ට %2$s වෙත ඉඩ දෙන්නද?"
+ "සැම විට <b>%1$s</b> හට %2$s වෙත ඉඩ දෙන්නද?"
+ "යෙදුම භාවිතා කරන විට පමණි"
+ "සැම විට"
+ "ප්රතික්ෂේප කරන්න, නැවත අසන්න එපා"
+ "%1$d අබලයි"
+ "සියල්ල අබලයි"
+ "කිසිවක් අබල නැත"
+ "ඉඩ දෙන්න"
+ "යෙදුම්"
+ "යෙදුම් අවසර"
+ "නැවත අසන්න එපා"
+ "අවසර නොමැත"
+ "අතිරේක අවසර"
+ "යෙදුම් තොරතුරු විවෘත කරන්න"
+
+ .තව %1$d
+ .තව %1$d
+
+ "මෙම යෙදුම නිර්මාණය කර ඇත්තේ Android වල පැරණි අනුවාදයකට වේ. අවසර නොදීම මඟින් එය බලාපොරොත්තු වන ආකාරයට වැඩ නොකිරීමට හැක."
+ "නොදන්නා ක්රියාවක් සිදු කරන්න"
+ "යෙදුම් %2$d න් %1$d කට ඉඩ දෙන ලදි"
+ "පද්ධතිය පෙන්වන්න"
+ "පද්ධතිය සඟවන්න"
+ "යෙදුම් නොමැත"
+ "ස්ථාන සැකසීම්"
+ "%1$s මෙම උපාංගය සඳහා ස්ථාන සේවාවන් සපයන්නෙකු වේ. ස්ථාන ප්රවේශය ස්ථාන සැකසීම් වෙතින් වෙනස් කළ හැක."
+ "ඔබ මෙම අවසරය ප්රතික්ෂේප කරන්නේ නම්, සමහර යෙදුම් බලාපොරොත්තු පරිදි ක්රියා නොකරනු ඇත."
+ "ප්රතිපත්තිය මඟින් බලාත්මක කරයි"
+ "පසුබිම් ප්රවේශය ප්රතිපත්තිය මගින් අබල කර ඇත"
+ "පසුබිම් ප්රවේශය ප්රතිපත්තිය මගින් සබල කර ඇත"
+ "පෙරබිම් ප්රවේශය ප්රතිපත්තිය මගින් සබල කර ඇත"
+ "පරිපාලක විසින් පාලනය කෙරේ"
+
+
+
+ "සැම විට"
+ "යෙදුම භාවිතා කරන විට පමණි"
+ "කිසි විටක නැත"
+ "පූරණය කරමින්…"
+ "සියලු අවසර"
+ "වෙනත් යෙදුම් හැකියාවන්"
+ "අවසර ඉල්ලීම"
+ "තිර උඩැතිරියක් අනාවරණය කරන ලදි"
+ "මෙම අවසර සැකසීම වෙනස් කිරීම සඳහා, ඔබට මුලින්ම සැකසීම් > යෙදුම් වෙතින් තිර උඩැතිරිය අක්රිය කර යුතුයි"
+ "සැකසීම් විවෘත කරන්න"
+ "Android Wear"
+ "Wear මත ස්ථාපන/අස්ථාපනය ක්රියා සහාය දක්වන්නේ නැත."
+ "<b>%1$s</b> හට පිවිසීමට ඉඩ දෙන දේ තෝරන්න"
+ "<b>%1$s</b> යාවත්කාලීන කර ඇත. මෙම යෙදුමට පිවිසීමට ඉඩ දෙන දේ තෝරන්න."
+ "අවලංගු කරන්න"
+ "දිගටම කර ගෙන යන්න"
+ "නව අවසර"
+ "වත්මන් අවසර"
+ "යෙදුම වේදිකාගත කරමින්..."
+ "නොදනී"
+ "ආරක්ෂාව සඳහා, ඔබගේ ටැබ්ලටය මෙම මුලාශ්රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."
+ "ආරක්ෂාව සඳහා, ඔබගේ රූපවාහිනිය මෙම මුලාශ්රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."
+ "ආරක්ෂාව සඳහා, ඔබගේ දුරකථනය මෙම මුලාශ්රයෙන් ලබාගත් නොදන්නා යෙදුම් ස්ථාපනය කිරීමට අවසර නැත."
+ "ඔබගේ දුරකථනය සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ දුරකථනය සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."
+ "ඔබගේ ටැබ්ලට් පරිගණකය සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ ටැබ්ලට් පරිගණකය සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."
+ "ඔබගේ TV සහ පුද්ගලික දත්තවලට නොදන්නා යෙදුම් මඟින් තර්ජන එල්ල කිරීමේ හැකියාව වැඩිය. මෙම යෙදුම් ස්ථාපනය කිරීමෙන් සහ භාවිත කිරීමෙන් ඔබ ඔබේ TV සඳහා සිදු වන යම් හානි හෝ එය භාවිත කිරීමේ ප්රතිඵලයක් ලෙස සිදු වන දත්ත හානි සඳහා ඔබ වගකිව යුතු බවට එකඟ වේ."
+ "දිගටම කරගෙන යන්න"
+ "සැකසීම්"
+ "Wear යෙදුම් ස්ථාපනය/අස්ථාපනය කරමින්"
+
diff --git a/packages/PackageInstaller/res/values-sk-television/strings.xml b/packages/PackageInstaller/res/values-sk-television/strings.xml
new file mode 100644
index 0000000000000..15ecdb890f5f1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Zamietnuť a nabudúce sa nepýtať"
+ "Neskôr to môžete zmeniť v časti Nastavenia > Aplikácie"
+ "%1$s / %2$s"
+ "Zobraziť systémové aplikácie"
+ "Povolenia aplikácie"
+ "Povolenia aplikácie"
+ "Povolenia – %1$s"
+ "Ďalšie povolenia"
+ "Povolenia – %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sk-watch/strings.xml b/packages/PackageInstaller/res/values-sk-watch/strings.xml
new file mode 100644
index 0000000000000..dc3ce4fae60e8
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Zamietnuť a už sa nepýtať"
+ "%1$s / %2$s"
+ "Zobraziť systémové aplikácie"
+ "Nedá sa zmeniť"
+ "Áno"
+ "Zrušiť"
+
diff --git a/packages/PackageInstaller/res/values-sk/strings.xml b/packages/PackageInstaller/res/values-sk/strings.xml
new file mode 100644
index 0000000000000..a27f6501faa3d
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sk/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Nástroj na inštaláciu balíčkov"
+ "Ďalej"
+ "Inštalovať"
+ "Hotovo"
+ "Zrušiť"
+ "Inštaluje sa..."
+ "Inštaluje sa %1$s…"
+ "Aplikácia bola nainštalovaná."
+ "Chcete nainštalovať túto aplikáciu? Získa nasledujúce povolenia:"
+ "Chcete nainštalovať túto aplikáciu? Nevyžaduje žiadny zvláštny prístup."
+ "Chcete nainštalovať aktualizáciu existujúcej aplikácie? Existujúce údaje sa nestratia. Aktualizovaná aplikácia získa nasledujúce povolenia:"
+ "Chcete nainštalovať aktualizáciu tejto integrovanej aplikácie? Existujúce údaje sa nestratia. Aktualizovaná aplikácia získa nasledujúce povolenia:"
+ "Chcete nainštalovať aktualizáciu tejto existujúcej aplikácie? Vaše údaje nebudú stratené. Táto akcia nevyžaduje žiadny zvláštny prístup."
+ "Chcete nainštalovať aktualizáciu tejto vstavanej aplikácie? Vaše údaje sa nestratia. Táto akcia nevyžaduje žiadny zvláštny prístup."
+ "Aplikácia nebola nainštalovaná."
+ "Inštalácia balíka bola zablokovaná."
+ "Aplikácia sa nenainštalovala, pretože balík koliduje s existujúcim balíkom."
+ "Aplikácia sa nenainštalovala, pretože nie je kompatibilná s vaším tabletom."
+ "Táto aplikácia nie je kompatibilná s vaším televízorom."
+ "Aplikácia sa nenainštalovala, pretože nie je kompatibilná s vaším telefónom."
+ "Aplikácia sa nenainštalovala, pretože je balík zrejme neplatný."
+ "Aplikáciu %1$s sa do vášho tabletu nepodarilo nainštalovať."
+ "Aplikáciu %1$s sa nepodarilo nainštalovať na vašom televízore."
+ "Aplikáciu %1$s sa do vášho telefónu nepodarilo nainštalovať."
+ "Otvoriť"
+ "Váš správca zakázal inštaláciu aplikácií z neznámych zdrojov"
+ "Tento používateľ nemôže inštalovať neznáme aplikácie"
+ "Tento používateľ nemá povolené inštalovať aplikácie"
+ "OK"
+ "Spravovať aplikácie"
+ "Nedostatok miesta"
+ "Aplikáciu %1$s sa nepodarilo nainštalovať. Uvoľnite miesto v pamäti a skúste to znova."
+ "Aplikácia sa nenašla"
+ "Aplikáciu sa nepodarilo nájsť v zozname nainštalovaných aplikácií."
+ "Nie je povolené"
+ "Aktuálny používateľ nemá na odinštalovanie povolenie."
+ "Chyba"
+ "Aplikáciu nie je možné odinštalovať."
+ "Odinštalovať aplikáciu"
+ "Odinštalovať aktualizáciu"
+ "Aktivita %1$s je súčasťou nasledujúcej aplikácie:"
+ "Chcete túto aplikáciu odinštalovať?"
+ "Chcete odinštalovať túto aplikáciu pre ""všetkých"" používateľov? Aplikácia a jej údaje sa odstránia z tohto zariadenia pre ""všetkých"" používateľov."
+ "Chcete túto aplikáciu odinštalovať pre používateľa %1$s?"
+ "Nahradiť túto aplikáciu továrenskou verziou? Všetky údaje sa odstránia."
+ "Nahradiť túto aplikáciu továrenskou verziou? Všetky údaje sa odstránia. Ovplyvní to všetkých používateľov tohto zariadenia vrátane tých s pracovnými profilmi."
+ "Prebiehajúce odinštalácie"
+ "Neúspešné odinštalácie"
+ "Prebieha odinštalovanie..."
+ "Prebieha odinštalovanie balíčka %1$s…"
+ "Odinštalovanie bolo dokončené."
+ "Balíček %1$s bol odinštalovaný"
+ "Odinštalovanie bolo neúspešné."
+ "Odinštalovanie balíčka %1$s sa nepodarilo."
+ "Aktívna aplikácia na správu zariadenia sa nedá odinštalovať"
+ "Aktívna aplikácia na správu zariadenia sa v prípade používateľa %1$s nedá odinštalovať"
+ "Táto aplikácia sa vyžaduje v prípade niektorých používateľov či profilov a v prípade iných zase bola odinštalovaná"
+ "Táto aplikácia sa vyžaduje pre váš profil a nemôžete ju odinštalovať."
+ "Túto aplikáciu vyžaduje správca vášho zariadenia a nie je ju možné odinštalovať."
+ "Spravovať aplikácie na ovládanie zariadenia"
+ "Spravovať používateľov"
+ "Aplikáciu %1$s sa nepodarilo odinštalovať."
+ "Pri analýze balíka sa vyskytol problém."
+ "Nové"
+ "Všetko"
+ "Ochrana súkromia"
+ "Prístup k zariadeniu"
+ "Táto aktualizácia nevyžaduje žiadne nové povolenia."
+ "Odmietnuť"
+ "Ďalšie informácie"
+ "Zamietnuť"
+ "%1$s z %2$s"
+ "Povoliť aplikácii <b>%1$s</b> %2$s?"
+ "Vždy povoliť aplikácii <b>%1$s</b> %2$s?"
+ "Iba počas používania aplikácie"
+ "Vždy"
+ "Zamietnuť a nabudúce sa nepýtať"
+ "deaktivované (%1$d)"
+ "všetky sú zakázané"
+ "žiadne nie sú zakázané"
+ "Povoliť"
+ "Aplikácie"
+ "Povolenia aplikácií"
+ "Nabudúce sa nepýtať"
+ "Žiadne povolenia"
+ "Ďalšie povolenia"
+ "Otvoriť informácie o aplikácii"
+
+ %1$d ďalšie
+ %1$d ďalšieho
+ %1$d ďalších
+ %1$d ďalšie
+
+ "Táto aplikácia bola navrhnutá pre staršiu verziu systému Android. Odmietnutie povolenia môže spôsobiť, že nebude optimálne fungovať."
+ "umožňuje vykonať neznámu akciu"
+ "Povolené %1$d z %2$d aplikácií"
+ "Zobraziť systémové aplikácie"
+ "Skryť systémové aplikácie"
+ "Žiadne aplikácie"
+ "Nastavenia polohy"
+ "Aplikácia %1$s je poskytovateľ služieb určovania polohy tohto zariadenia. Prístup k polohe môžete upraviť v nastaveniach polohy."
+ "Ak toto povolenie zamietnete, základné funkcie vášho zariadenia nemusia pracovať podľa očakávaní."
+ "Vynútené pravidlom"
+ "Prístup na pozadí je zakázaný pravidlom"
+ "Prístup na pozadí je povolený pravidlom"
+ "Prístup na popredí je povolený pravidlom"
+ "Ovládané správcom"
+
+
+
+ "Vždy"
+ "Iba počas používania aplikácie"
+ "Nikdy"
+ "Načítava sa…"
+ "Všetky povolenia"
+ "Ďalšie možnosti aplikácie"
+ "Žiadosť o povolenie"
+ "Bolo zistené prekrytie obrazovky"
+ "Ak chcete zmeniť nastavenie tohto povolenia, musíte najprv v časti Nastavenia > Aplikácie vypnúť prekrytie obrazovky"
+ "Otvoriť nastavenia"
+ "Android Wear"
+ "Systém Wear nepodporuje akciu inštalácie/odinštalovania."
+ "Vyberte, k čomu môže pristupovať aplikácia <b>%1$s</b>"
+ "Aplikácia <b>%1$s</b> bola aktualizovaná. Vyberte, k čomu môže pristupovať."
+ "Zrušiť"
+ "Pokračovať"
+ "Nové povolenia"
+ "Aktuálne povolenia"
+ "Aplikácia je zavádzaná po etapách…"
+ "Neznáma"
+ "Váš tablet nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."
+ "Váš televízor nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."
+ "Váš telefón nemôže z bezpečnostných dôvodov inštalovať neznáme aplikácie z tohto zdroja."
+ "Váš telefón a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho telefónu či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."
+ "Váš tablet a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho tabletu či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."
+ "Váš televízor a osobné údaje sú náchylnejšie na útok z neznámych aplikácií. Inštalovaním tejto aplikácie súhlasíte, že zodpovedáte za akékoľvek poškodenie vášho televízora či stratu údajov, ku ktorým môže dôjsť v dôsledku jej použitia."
+ "Pokračovať"
+ "Nastavenia"
+ "Inštalácia/odinštalácia aplikácií Wear"
+
diff --git a/packages/PackageInstaller/res/values-sl-television/strings.xml b/packages/PackageInstaller/res/values-sl-television/strings.xml
new file mode 100644
index 0000000000000..3e8e4ea5294e6
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Zavrni in ne sprašuj več"
+ "To lahko pozneje spremenite v »Nastavitve > Aplikacije«"
+ "%1$s/%2$s"
+ "Prikaz sistemskih aplikacij"
+ "Dovoljenja za aplikacije"
+ "Dovoljenja za aplikacije"
+ "Dovoljenja za: %1$s"
+ "Dodatna dovoljenja"
+ "Dovoljenja za: %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sl-watch/strings.xml b/packages/PackageInstaller/res/values-sl-watch/strings.xml
new file mode 100644
index 0000000000000..906a5519c80dd
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Zavrni, ne sprašuj več"
+ "%1$s/%2$s"
+ "Prikaz sistemskih aplikacij"
+ "Ni mogoče sprem."
+ "Da"
+ "Prekliči"
+
diff --git a/packages/PackageInstaller/res/values-sl/strings.xml b/packages/PackageInstaller/res/values-sl/strings.xml
new file mode 100644
index 0000000000000..4075006df417f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sl/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Namestitveno orodje za paket"
+ "Naprej"
+ "Namesti"
+ "Dokončano"
+ "Prekliči"
+ "Nameščanje …"
+ "Nameščanje aplikacije %1$s …"
+ "Aplikacija je nameščena."
+ "Ali želite namestiti to aplikacijo? Imela bo dostop do:"
+ "Ali želite namestiti to aplikacijo? Poseben dostop ni potreben."
+ "Ali želite namestiti posodobitev te obstoječe aplikacije? Obstoječi podatki ne bodo izgubljeni. Posodobljena aplikacija bo imela dostop do:"
+ "Ali želite namestiti posodobitev za to vgrajeno aplikacijo? Obstoječi podatki ne bodo izgubljeni. Posodobljena aplikacija bo imela dostop do:"
+ "Ali želite namestiti posodobitev te obstoječe aplikacije? Obstoječi podatki ne bodo izgubljeni. Za namestitev ne potrebujete posebnega dostopa."
+ "Ali želite namestiti posodobitev te vgrajene aplikacije? Obstoječi podatki ne bodo izgubljeni. Za namestitev ne potrebujete posebnega dostopa."
+ "Aplikacija ni nameščena."
+ "Namestitev paketa je bila blokirana."
+ "Aplikacija ni bila nameščena, ker je paket v navzkrižju z obstoječim paketom."
+ "Aplikacija ni bila nameščena, ker ni združljiva s tabličnim računalnikom."
+ "Ta aplikacija ni združljiva z vašim televizorjem."
+ "Aplikacija ni bila nameščena, ker ni združljiva s telefonom."
+ "Aplikacija ni bila nameščena, ker paket verjetno ni veljaven."
+ "Aplikacije %1$s ni bilo mogoče namestiti v tablični računalnik."
+ "Aplikacije %1$s ni bilo mogoče namestiti v vašem televizorju."
+ "Aplikacije %1$s ni bilo mogoče namestiti v telefon."
+ "Odpri"
+ "Skrbnik ne dovoli nameščanja aplikacij iz neznanih virov."
+ "Ta uporabnik nima dovoljenja za nameščanje neznanih aplikacij"
+ "Ta uporabnik nima dovoljenja za nameščanje aplikacij"
+ "V redu"
+ "Upravljaj aplikacije"
+ "Zmanjkalo je prostora"
+ "Aplikacije %1$s ni bilo mogoče namestiti. Sprostite prostor in poskusite znova."
+ "Aplikacije ni bilo mogoče najti"
+ "Aplikacije ni bilo mogoče najti na seznamu nameščenih aplikacij."
+ "Ni dovoljeno"
+ "Trenutni uporabnik nima dovoljenja za izvedbo te odstranitve."
+ "Napaka"
+ "Aplikacije ni bilo mogoče odstraniti."
+ "Odstrani aplikacijo"
+ "Odstrani posodobitev"
+ "%1$s je del te aplikacije:"
+ "Ali želite odstraniti to aplikacijo?"
+ "Ali želite odstraniti aplikacijo za ""vse"" uporabnike? Aplikacija in njeni podatki bodo odstranjeni iz ""vseh"" uporabnikov v napravi."
+ "Ali želite to aplikacijo odstraniti za uporabnika %1$s?"
+ "Želite to aplikacijo nadomestiti s tovarniško različico? Odstranjeni bodo vsi podatki."
+ "Želite to aplikacijo nadomestiti s tovarniško različico? Odstranjeni bodo vsi podatki. To vpliva na vse uporabnike te naprave, vključno s tistimi z delovnimi profili."
+ "Odstranitve v teku"
+ "Neuspele odstranitve"
+ "Odstranjevanje ..."
+ "Odstranjevanje aplikacije %1$s …"
+ "Odstranitev je končana."
+ "Aplikacija %1$s je bila odstranjena"
+ "Odstranitev ni uspela."
+ "Odstranjevanje aplikacije %1$s ni uspelo."
+ "Aktivne skrbniške aplikacije naprave ni mogoče odstraniti"
+ "Aktivne skrbniške aplikacije za uporabnika %1$s ni mogoče odstraniti"
+ "Aplikacija je obvezna za nekatere uporabnike/profile in je odstranjena za druge."
+ "Ta aplikacija je potrebna za profil in je ni mogoče odstraniti."
+ "To aplikacijo zahteva skrbnik naprave in je ni mogoče odstraniti."
+ "Upravljanje skrbniških aplikacij naprave"
+ "Upravljanje uporabnikov"
+ "Aplikacije %1$s ni bilo mogoče odstraniti."
+ "Težava pri razčlenjevanju paketa."
+ "Novo"
+ "Vse"
+ "Zasebnost"
+ "Dostop do naprave"
+ "Za to posodobitev niso potrebna nova dovoljenja."
+ "Zavrni"
+ "Več informacij"
+ "Vseeno zavrni"
+ "%1$s od %2$s"
+ "Ali dovolite aplikaciji <b>%1$s</b> izvesti to dejanje: %2$s?"
+ "Želite aplikaciji <b>%1$s</b> vedno dovoliti to dejanje: %2$s?"
+ "Samo med uporabo aplikacije"
+ "Vedno"
+ "Zavrni in ne sprašuj več"
+ "št. onemogočenih: %1$d"
+ "vse onemogočeno"
+ "nič ni onemogočeno"
+ "Dovoli"
+ "Aplikacije"
+ "Dovoljenja za aplikacije"
+ "Ne sprašuj več"
+ "Ni dovoljenj"
+ "Dodatna dovoljenja"
+ "Odpri podatke o aplikaciji"
+
+ Še %1$d
+ Še %1$d
+ Še %1$d
+ Še %1$d
+
+ "Ta aplikacija je bila zasnovana za starejšo različico sistema Android. Če dovoljenje zavrnete, lahko preneha delovati, kot bi morala."
+ "izvedba neznanega dejanja"
+ "Dovoljene aplikacije: %1$d od %2$d"
+ "Prikaz sistemskih aplikacij"
+ "Skrivanje sistemskih aplikacij"
+ "Ni aplikacij"
+ "Nastavitve lokacije"
+ "%1$s je ponudnik lokacijskih storitev za to napravo. Dostop do lokacije je mogoče spremeniti v nastavitvah lokacije."
+ "Če zavrnete to dovoljenje, osnovne funkcije naprave morda ne bodo več delovale, kot bi morale."
+ "Uveljavlja pravilnik"
+ "Dostop iz ozadja je onemogočen s pravilnikom"
+ "Dostop iz ozadja je omogočen s pravilnikom"
+ "Dostop v ospredju je omogočen s pravilnikom"
+ "Nadzira skrbnik"
+
+
+
+ "Vedno"
+ "Samo med uporabo aplikacije"
+ "Nikoli"
+ "Nalaganje …"
+ "Vsa dovoljenja"
+ "Druge zmožnosti aplikacije"
+ "Zahteva za dovoljenje"
+ "Zaznano prekrivanje zaslona"
+ "Če želite spremeniti nastavitev tega dovoljenja, morate najprej izklopiti prekrivanje zaslona v »Nastavitve > Aplikacije«"
+ "Odpri nastavitve"
+ "Android Wear"
+ "Dejanja namestitve in odstranitve v sistemu Android Wear niso podprta."
+ "Izberite, do česa aplikaciji <b>%1$s</b> dovolite dostop"
+ "Aplikacija <b>%1$s</b> je posodobljena. Izberite, do česa tej aplikaciji dovolite dostop."
+ "Prekliči"
+ "Naprej"
+ "Nova dovoljenja"
+ "Trenutna dovoljenja"
+ "Priprava aplikacije …"
+ "Neznano"
+ "Vaš tablični računalnik zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."
+ "Vaš televizor zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."
+ "Vaš telefon zaradi varnosti nima dovoljenja za nameščanje neznanih aplikacij iz tega vira."
+ "Neznane aplikacije lahko resno ogrozijo varnost telefona in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v telefonu, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."
+ "Neznane aplikacije lahko resno ogrozijo varnost tabličnega računalnika in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v tabličnem računalniku, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."
+ "Neznane aplikacije lahko resno ogrozijo varnost televizorja in osebnih podatkov. Z namestitvijo te aplikacije se strinjate, da ste sami odgovorni za morebitno škodo, nastalo v televizorju, ali izgubo podatkov, do katerih lahko pride zaradi uporabe te aplikacije."
+ "Nadaljuj"
+ "Nastavitve"
+ "Nameščanje/odstranjev. aplikacij za Wear"
+
diff --git a/packages/PackageInstaller/res/values-sq-television/strings.xml b/packages/PackageInstaller/res/values-sq-television/strings.xml
new file mode 100644
index 0000000000000..d66231cbaf3f5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Refuzo dhe mos pyet përsëri"
+ "Këtë mund ta ndryshosh më vonë te Cilësimet > Aplikacionet"
+ "%1$s / %2$s"
+ "Shfaq aplikacionet e sistemit"
+ "Lejet e aplikacionit"
+ "Lejet e aplikacionit"
+ "Lejet për %1$s"
+ "Lejet shtesë"
+ "Lejet për %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sq-watch/strings.xml b/packages/PackageInstaller/res/values-sq-watch/strings.xml
new file mode 100644
index 0000000000000..772bb7aab7fcf
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Refuzoje, mos pyet sërish"
+ "%1$s / %2$s"
+ "Shfaq aplikacionet e sistemit"
+ "Nuk mund të ndryshohet"
+ "Po"
+ "Anulo"
+
diff --git a/packages/PackageInstaller/res/values-sq/strings.xml b/packages/PackageInstaller/res/values-sq/strings.xml
new file mode 100644
index 0000000000000..8f7e0fce9f31e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sq/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Instaluesi i paketës"
+ "Përpara"
+ "Instalo"
+ "U krye!"
+ "Anulo"
+ "Po instalon…"
+ "Po instalon %1$s…"
+ "Aplikacioni u instalua."
+ "Dëshiron ta instalosh këtë aplikacion? Ai do të ketë qasje në:"
+ "Dëshiron ta instalosh këtë aplikacion? Nuk kërkon ndonjë qasje të veçantë."
+ "Dëshiron të instalosh një përditësim në këtë aplikacion ekzistues? Të dhënat e tua ekzistuese nuk do të humbin. Aplikacioni i përditësuar do të ketë qasje në:"
+ "Dëshiron të instalosh një përditësim në këtë aplikacion të integruar? Të dhënat e tua ekzistuese nuk do të humbin. Aplikacioni i përditësuar do të ketë qasje në:"
+ "Dëshiron të instalosh një përditësim të këtij aplikacioni ekzistues? Të dhënat e tua ekzistuese nuk do të humbasin. Aplikacioni nuk kërkon ndonjë qasje të veçantë."
+ "Dëshiron të instalosh një përditësim të këtij aplikacioni ekzistues? Të dhënat e tua ekzistuese nuk do të humbasin. Aplikacioni nuk kërkon ndonjë qasje të veçantë."
+ "Aplikacioni nuk u instalua."
+ "Instalimi paketës u bllokua."
+ "Aplikacioni nuk u instalua pasi paketa është në konflikt me një paketë ekzistuese."
+ "Aplikacioni nuk u instalua pasi nuk është i përputhet me tabletin tënd."
+ "Ky aplikacion është i papërshtatshëm me televizorin tënd."
+ "Aplikacioni nuk u instalua pasi nuk përputhet me telefonin tënd."
+ "Aplikacioni nuk u instalua pasi paketa duket se nuk është e vlefshme."
+ "%1$s nuk mund të instalohej në tablet."
+ "%1$s nuk mund të instalohej në televizor."
+ "%1$s nuk mundi të instalohej në telefon."
+ "Hap"
+ "Administratori nuk lejon instalimin e aplikacioneve nga burime të panjohura."
+ "Aplikacionet e panjohura nuk mund të instalohen nga ky përdorues"
+ "Ky përdorues nuk lejohet të instalojë aplikacione"
+ "Në rregull"
+ "Menaxho aplikacionet"
+ "Nuk ka hapësirë"
+ "%1$s nuk mund të instalohej. Liro pak hapësirë dhe provo përsëri."
+ "Aplikacioni nuk u gjet"
+ "Aplikacioni nuk u gjet në listën e aplikacioneve të instaluara."
+ "Nuk lejohet"
+ "Përdoruesi aktual nuk lejohet të kryejë këtë çinstalim."
+ "Gabim"
+ "Aplikacioni nuk mund të instalohej."
+ "Çinstalo aplikacionin"
+ "Çinstalo përditësimin"
+ "%1$s është pjesë e aplikacionit të mëposhtëm:"
+ "Dëshiron ta çinstalosh këtë aplikacion?"
+ "Dëshiron ta çinstalosh këtë aplikacion për ""të gjithë"" përdoruesit? Aplikacioni dhe të dhënat e tij do të hiqen nga ""të gjithë"" përdoruesit e pajisjes."
+ "Dëshiron ta çinstalosh këtë aplikacion për përdoruesin %1$s?"
+ "Të zëvendësohet ky aplikacion me versionin e fabrikës? Të gjitha të dhënat do të hiqen."
+ "Të zëvendësohet ky aplikacion me versionin e fabrikës? Të gjitha të dhënat do të hiqen. Kjo ndikon te të gjithë përdoruesit e kësaj pajisjeje, duke përfshirë ata me profile të punës."
+ "Çinstalimet në ekzekutim"
+ "Çinstalimet e dështuara"
+ "Po e çinstalon…"
+ "%1$s po çinstalohet…"
+ "Çinstalimi përfundoi."
+ "%1$s u çinstalua"
+ "Çinstalimi nuk pati sukses."
+ "Çinstalimi i %1$s nuk u krye me sukses."
+ "Nuk mund të çinstalohet aplikacioni aktiv i administratorit të pajisjes"
+ "Nuk mund të çinstalohet aplikacioni aktiv i administratorit të pajisjes për %1$s"
+ "Ky aplikacion kërkohet për disa përdorues ose profile dhe është çinstaluar për të tjerët"
+ "Ky aplikacion nevojitet për profilin tënd dhe nuk mund të çinstalohet."
+ "Ky aplikacion kërkohet nga administratori i pajisjes dhe nuk mund të çinstalohet."
+ "Menaxho aplikacionet e administratorit të pajisjes"
+ "Menaxho përdoruesit"
+ "%1$s nuk mundi të çinstalohej."
+ "Kishte një problem me analizimin e paketës."
+ "E re"
+ "Të gjitha"
+ "Privatësia"
+ "Qasja në pajisje"
+ "Ky përditësim nuk kërkon leje të reja."
+ "Refuzo"
+ "Informacione të tjera"
+ "Refuzo sidoqoftë"
+ "%1$s nga %2$s"
+ "Të lejohet <b>%1$s</b> që të %2$s?"
+ "Të lejohet gjithmonë <b>%1$s</b> që %2$s?"
+ "Vetëm gjatë përdorimit të aplikacionit"
+ "Gjithmonë"
+ "Refuzo dhe mos pyet përsëri"
+ "%1$d të çaktivizuara"
+ "të gjitha të çaktivizuara"
+ "asnjë e çaktivizuar"
+ "Lejo"
+ "Aplikacionet"
+ "Lejet e aplikacionit"
+ "Mos pyet përsëri"
+ "Nuk ka leje"
+ "Lejet shtesë"
+ "Hap informacionet e aplikacionit"
+
+ %1$d të tjera
+ %1$d të tjera
+
+ "Ky aplikacion është projektuar për një version më të vjetër të Android. Refuzimi i lejeve mund të shkaktojë që ai të mos funksionojë më siç duhet."
+ "kryej një veprim të panjohur"
+ "%1$d aplikacione nga %2$d të tilla u lejuan"
+ "Shfaq sistemin"
+ "Fshih sistemin"
+ "Asnjë aplikacion"
+ "Cilësimet e vendndodhjeve"
+ "%1$s është një ofrues i shërbimeve të vendndodhjes për këtë pajisje. Qasja e vendndodhjes mund të modifikohet nga cilësimet e vendndodhjes."
+ "Nëse e refuzon këtë leje, funksionet bazë të pajisjes tënde mund të mos funksionojnë më siç pritet."
+ "Zbatuar nga politika"
+ "Qasja në sfond është e çaktivizuar sipas politikës"
+ "Qasja në sfond është e aktivizuar sipas politikës"
+ "Qasja në planin e parë është e aktivizuar sipas politikës"
+ "Kontrolluar nga administratori"
+
+
+
+ "Gjithmonë"
+ "Vetëm gjatë përdorimit të aplikacionit"
+ "Asnjëherë"
+ "Po ngarkon..."
+ "Të gjitha lejet"
+ "Kapacitete të tjera të aplikacionit"
+ "Kërkesa e lejes"
+ "Mbivendosja e ekranit u zbulua"
+ "Për të ndryshuar këtë cilësim të lejes, në fillim duhet të çaktivizosh mbivendosjen e ekranit nga Cilësimet > Aplikacionet"
+ "Hap cilësimet"
+ "Teknologjia \"Android\" që vishet"
+ "Instalo/çinstalo veprimet që nuk mbështeten në teknologjinë që vishet."
+ "Zgjidh se ku do të lejohet të ketë qasje <b>%1$s</b>"
+ "<b>%1$s</b> është përditësuar. Zgjidh se ku do të lejohet të ketë qasje ky aplikacion."
+ "Anulo"
+ "Vazhdo"
+ "Lejet e reja"
+ "Lejet aktuale"
+ "Po vihet në përdorim aplikacioni..."
+ "E panjohur"
+ "Për sigurinë tënde, tableti yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."
+ "Për sigurinë tënde, televizori yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."
+ "Për sigurinë tënde, telefoni yt nuk lejohet të instalojë aplikacione të panjohura nga ky burim."
+ "Telefoni dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj telefonit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."
+ "Tableti dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj tabletit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."
+ "Televizori dhe të dhënat e tua personale janë më të cenueshme për t\'u sulmuar nga aplikacione të panjohura. Duke instaluar këtë aplikacion, ti pranon se je përgjegjës për çdo dëm ndaj televizorit tënd ose çdo humbje të dhënash që mund të rezultojë nga përdorimi i tij."
+ "Vazhdo"
+ "Cilësimet"
+ "Instalimi/çinstalimi i aplikacioneve të \"Wear\""
+
diff --git a/packages/PackageInstaller/res/values-sr-television/strings.xml b/packages/PackageInstaller/res/values-sr-television/strings.xml
new file mode 100644
index 0000000000000..fcfe15666c704
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Одбиј и не питај поново"
+ "Ово можете да промените касније у Подешавањима > Апликације"
+ "%1$s/%2$s"
+ "Прикажи системске апликације"
+ "Дозволе за апликације"
+ "Дозволе за апликације"
+ "Дозволе за апликацију %1$s"
+ "Додатне дозволе"
+ "Дозволе за апликацију %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sr-watch/strings.xml b/packages/PackageInstaller/res/values-sr-watch/strings.xml
new file mode 100644
index 0000000000000..7e15b013d37af
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Одбиј и не питај поново"
+ "%1$s/%2$s"
+ "Прикажи системске апликације"
+ "Не може да се промени"
+ "Да"
+ "Откажи"
+
diff --git a/packages/PackageInstaller/res/values-sr/strings.xml b/packages/PackageInstaller/res/values-sr/strings.xml
new file mode 100644
index 0000000000000..494b0d4e22d22
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sr/strings.xml
@@ -0,0 +1,157 @@
+
+
+
+
+ "Упаковани програм за инсталацију"
+ "Даље"
+ "Инсталирај"
+ "Готово"
+ "Откажи"
+ "Инсталирање..."
+ "Инсталира се %1$s…"
+ "Апликација је инсталирана."
+ "Желите ли да инсталирате ову апликацију? Имаће приступ следећем:"
+ "Желите ли да инсталирате ову апликацију? Не захтева посебан приступ."
+ "Желите ли да инсталирате ажурирање за ову постојећу апликацију? Постојећи подаци неће бити изгубљени. Ажурирана апликација имаће приступ следећем:"
+ "Желите ли да инсталирате ажурирање за ову уграђену апликацију? Постојећи подаци неће бити изгубљени. Ажурирана апликација ће имати приступ следећем:"
+ "Да ли желите да инсталирате ажурирање ове постојеће апликације? Постојећи подаци неће бити изгубљени. Није потребан посебан приступ."
+ "Да ли желите да инсталирате ажурирање ове уграђене апликације? Постојећи подаци неће бити изгубљени. Није потребан посебан приступ."
+ "Апликација није инсталирана."
+ "Инсталирање пакета је блокирано."
+ "Апликација није инсталирана јер је пакет неусаглашен са постојећим пакетом."
+ "Апликација није инсталирана јер није компатибилна са таблетом."
+ "Ова апликација није компатибилна са ТВ-ом."
+ "Апликација није инсталирана јер није компатибилна са телефоном."
+ "Апликација није инсталирана јер је пакет неважећи."
+ "Није могуће инсталирати апликацију %1$s на таблет."
+ "Нисмо успели да инсталирамо %1$s на ТВ."
+ "Није могуће инсталирати апликацију %1$s на телефон."
+ "Отвори"
+ "Администратор не дозвољава инсталирање апликација добијених из непознатих извора"
+ "Овај корисник не може да инсталира непознате апликације"
+ "Овом кориснику није дозвољено да инсталира апликације"
+ "Потврди"
+ "Управљање апликацијама"
+ "Нема више места"
+ "Није могуће инсталирати апликацију %1$s. Ослободите додатни простор и покушајте поново."
+ "Апликација није пронађена"
+ "Апликација није пронађена на листи инсталираних апликација."
+ "Није дозвољено"
+ "Актуелном кориснику није дозвољено да обави ово деинсталирање."
+ "Грешка"
+ "Деинсталирање апликације није успело."
+ "Деинсталирање апликације"
+ "Деинсталирање ажурирања"
+ "%1$s је део следеће апликације:"
+ "Да ли желите да деинсталирате ову апликацију?"
+ "Да ли желите да деинсталирате ову апликацију за ""све"" кориснике? Апликација и подаци који се на њу односе биће уклоњени за ""све"" кориснике овог уређаја."
+ "Желите ли да деинсталирате ову апликацију за корисника %1$s?"
+ "Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени."
+ "Желите ли да замените ову апликацију фабричком верзијом? Сви подаци ће бити уклоњени. Ово утиче на све кориснике овог уређаја, укључујући и оне са профилима за Work."
+ "Активна деинсталирања"
+ "Неуспела деинсталирања"
+ "Деинсталирање..."
+ "%1$s се деинсталира…"
+ "Деинсталирање је завршено."
+ "Апликација %1$s је деинсталирана"
+ "Деинсталирање није успело."
+ "Деинсталирање апликације %1$s није успело."
+ "Не можете да деинсталирате апликацију за активног администратора уређаја"
+ "Не можете да деинсталирате апликацију за активног администратора уређаја за %1$s"
+ "Ова апликација је потребна за неке кориснике или профиле, а деинсталирана је за друге"
+ "Ова апликација је потребна за ваш профил и не може да се деинсталира."
+ "Ова апликација је потребна администратору уређаја и не може да се деинсталира."
+ "Управљај апликацијама за администраторе уређаја"
+ "Управљаj корисницима"
+ "Није могуће деинсталирати апликацију %1$s."
+ "Дошло је до проблема при рашчлањивању пакета."
+ "Ново"
+ "Све"
+ "Приватност"
+ "Приступ уређају"
+ "Ово ажурирање не захтева нове дозволе."
+ "Одбаци"
+ "Више информација"
+ "Ипак одбиј"
+ "%1$s. од %2$s"
+ "Желите ли да дозволите да <b>%1$s</b> %2$s?"
+ "Желите ли увек да дозволите да <b>%1$s</b> %2$s?"
+ "Само док се апликација користи"
+ "Увек"
+ "Одбиј и не питај поново"
+ "Онемогућених: %1$d"
+ "све су онемогућене"
+ "ниједна није онемогућена"
+ "Дозволи"
+ "Апликације"
+ "Дозволе за апликације"
+ "Не питај поново"
+ "Нема дозвола"
+ "Додатне дозволе"
+ "Отвори информације о апликацији"
+
+ још %1$d
+ још %1$d
+ још %1$d
+
+ "Ова апликација је дизајнирана за старију верзију Android-а. Ако одбијете дозволу, она можда више неће правилно да функционише."
+ "обавља непознату радњу"
+ "%1$d од %2$d апликација има дозволу"
+ "Прикажи системске"
+ "Сакриј системске"
+ "Нема апликација"
+ "Подешавања локације"
+ "%1$s пружа услуге локације за овај уређај. Приступ локацији можете да измените у подешавањима локације."
+ "Ако одбијете ову дозволу, основне функције уређаја можда неће више функционисати исправно."
+ "Примењује се у складу са смерницама"
+ "Приступ у позадини је онемогућен смерницама"
+ "Приступ у позадини је омогућен смерницама"
+ "Приступ у првом плану је омогућен смерницама"
+ "Контролише администратор"
+
+
+
+ "Увек"
+ "Само док се апликација користи"
+ "Никада"
+ "Учитава се…"
+ "Све дозволе"
+ "Остале могућности апликације"
+ "Захтев за дозволу"
+ "Откривен је елемент који прекрива садржај екрана"
+ "Да бисте променили подешавање ове дозволе, прво треба да искључите елемент који прекрива садржај екрана у одељку Подешавања > Апликације"
+ "Отвори подешавања"
+ "Android Wear"
+ "Радње Инсталирај/Деинсталирај нису подржане у Wear-у."
+ "Изаберите чему <b>%1$s</b> може да приступа"
+ "Апликација <b>%1$s</b> је ажурирана. Изаберите чему ова апликација може да приступа."
+ "Откажи"
+ "Настави"
+ "Нове дозволе"
+ "Актуелне дозволе"
+ "Апликација се припрема…"
+ "Непознато"
+ "Таблету из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."
+ "Телевизору из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."
+ "Телефону из безбедносних разлога није дозвољено да инсталира непознате апликације из овог извора."
+ "Телефон и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења телефона или губитак података до којих може да дође због њеног коришћења."
+ "Таблет и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења таблета или губитак података до којих може да дође због њеног коришћења."
+ "ТВ и лични подаци су подложнији нападу непознатих апликација. Ако инсталирате ову апликацију, прихватате да сте одговорни за евентуална оштећења ТВ-а или губитак података до којих може да дође због њеног коришћења."
+ "Настави"
+ "Подешавања"
+ "Инсталирање/деинсталирање Wear апликација"
+
diff --git a/packages/PackageInstaller/res/values-sv-television/strings.xml b/packages/PackageInstaller/res/values-sv-television/strings.xml
new file mode 100644
index 0000000000000..5f237675336f2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Neka och fråga inte igen"
+ "Du kan ändra detta senare i Inställningar > Appar"
+ "%1$s/%2$s"
+ "Visa systemappar"
+ "Appens behörigheter"
+ "Appens behörigheter"
+ "Behörigheter för %1$s"
+ "Ytterligare behörigheter"
+ "Behörigheter för %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sv-watch/strings.xml b/packages/PackageInstaller/res/values-sv-watch/strings.xml
new file mode 100644
index 0000000000000..f20ec21b6cb77
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Avvisa och fråga inte igen"
+ "%1$s/%2$s"
+ "Visa systemappar"
+ "Kan inte ändras"
+ "Ja"
+ "Avbryt"
+
diff --git a/packages/PackageInstaller/res/values-sv/strings.xml b/packages/PackageInstaller/res/values-sv/strings.xml
new file mode 100644
index 0000000000000..ff4f4c15d30b9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sv/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Installationsprogram för paket"
+ "Nästa"
+ "Installera"
+ "Färdig"
+ "Avbryt"
+ "Installerar…"
+ "%1$s installeras …"
+ "Appen har installerats."
+ "Vill du installera den här appen? Den får åtkomst till:"
+ "Vill du installera den här appen? Den kräver ingen särskild åtkomst."
+ "Vill du installera en uppdatering till den här befintliga appen? Dina befintliga data försvinner inte. Den uppdaterade appen får åtkomst till:"
+ "Vill du installera en uppdatering till den här befintliga förinstallerade appen? Dina befintliga data försvinner inte. Den uppdaterade appen får åtkomst till:"
+ "Vill du installera en uppdatering av den befintliga appen? Dina befintliga data försvinner inte. Ingen särskild åtkomst krävs."
+ "Vill du installera en uppdatering av den inbyggda appen? Dina befintliga data försvinner inte. Ingen särskild åtkomst krävs."
+ "Appen har inte installerats."
+ "Paketet har blockerats för installation."
+ "Appen har inte installerats på grund av en konflikt mellan detta paket och ett befintligt paket."
+ "Appen har inte installerats eftersom den inte är kompatibel med surfplattan."
+ "Appen är inte kompatibel med din TV."
+ "Appen har inte installerats eftersom den inte är kompatibel med mobilen."
+ "Appen har inte installerats eftersom paketet verkar vara ogiltigt."
+ "Det gick inte att installera %1$s på surfplattan."
+ "%1$s kunde inte installeras på TV:n."
+ "Det gick inte att installera %1$s på mobilen."
+ "Öppna"
+ "Administratören tillåter inte installation av appar från okända källor"
+ "Denna användare får inte installera okända appar"
+ "Användaren har inte behörighet att installera appar"
+ "OK"
+ "Hantera appar"
+ "Slut på utrymme"
+ "Det gick inte att installera %1$s. Frigör minne och försök igen."
+ "Appen hittades inte"
+ "Appen fanns inte i listan över installerade appar."
+ "Ingen behörighet"
+ "Den aktuella användaren har inte behörighet att utföra avinstallationen."
+ "Fel"
+ "Det gick inte att installera appen."
+ "Avinstallera appen"
+ "Avinstallera uppdateringen"
+ "%1$s är en del av följande app:"
+ "Vill du avinstallera appen?"
+ "Vill du avinstallera den här appen för ""alla"" användare? Appen och alla data i den tas bort från ""alla"" användare på enheten."
+ "Vill du avinstallera appen för användaren %1$s?"
+ "Vill du ersätta den här appen med den version som var installerad när enheten var ny? All information tas bort."
+ "Vill du ersätta den här appen med den version som var installerad när enheten var ny? All information tas bort. Detta påverkar alla som använder enheten, även dem med jobbprofiler."
+ "Avinstallationer som pågår"
+ "Avinstallationer som misslyckats"
+ "Avinstallerar…"
+ "%1$s avinstalleras …"
+ "Avinstallationen har slutförts."
+ "%1$s har avinstallerats"
+ "Det gick inte att avinstallera."
+ "Det gick inte att avinstallera %1$s."
+ "Det går inte att avinstallera den aktiva appen för enhetsadministratör"
+ "Det går inte att avinstallera den aktiva appen för enhetsadministratör för %1$s"
+ "Den här appen är obligatorisk för vissa användare och profiler och har avinstallerats för andra"
+ "Appen behövs i profilen och det går inte att avinstallera den"
+ "Appen krävs av enhetsadministratören och kan därför inte avinstalleras."
+ "Hantera appar för enhetsadministratör"
+ "Hantera användare"
+ "Det gick inte att avinstallera %1$s."
+ "Ett problem uppstod när paketet analyserades."
+ "Nytt"
+ "Alla"
+ "Sekretess"
+ "Enhetsåtkomst"
+ "Det krävs inga nya behörigheter för den här uppdateringen."
+ "Neka"
+ "Mer information"
+ "Neka ändå"
+ "%1$s av %2$s"
+ "Tillåter du <b>%1$s</b> att %2$s?"
+ "Vill du alltid tillåta att <b>%1$s</b> %2$s?"
+ "Bara när appen används"
+ "Alltid"
+ "Neka och fråga inte igen"
+ "%1$d har inaktiverats"
+ "alla har inaktiverats"
+ "inga har inaktiverats"
+ "Tillåt"
+ "Appar"
+ "Appens behörigheter"
+ "Fråga inte igen"
+ "Inga behörigheter"
+ "Ytterligare behörigheter"
+ "Öppna appinformation"
+
+ %1$d till
+ %1$d till
+
+ "Den här appen utformades för en äldre version av Android. Om du nekar appen behörighet kan det hända att den inte längre fungerar som den ska."
+ "utför en okänd åtgärd"
+ "%1$d av %2$d appar tillåts"
+ "Visa systemet"
+ "Dölj systemet"
+ "Inga appar"
+ "Platsinställningar"
+ "%1$s är en platstjänstleverantör för enheten. Platsåtkomsten kan redigeras i platsinställningarna."
+ "Om du nekar appen behörighet kan det hända att grundläggande funktioner på enheten inte fungerar som de ska."
+ "Enligt policyn"
+ "Åtkomst i bakgrunden har inaktiverats av en princip"
+ "Åtkomst i bakgrunden har aktiverats av en princip"
+ "Åtkomst i förgrunden har aktiverats av en princip"
+ "Styrs av administratören"
+
+
+
+ "Alltid"
+ "Bara när appen används"
+ "Aldrig"
+ "Läser in …"
+ "Alla behörigheter"
+ "Andra appbehörigheter"
+ "Begäran om behörighet"
+ "Skärmöverlagring har upptäckts"
+ "Innan du kan ändra den här behörighetsinställningen måste du inaktivera skärmöverlagring under Inställningar > Appar"
+ "Öppna inställningarna"
+ "Android Wear"
+ "Åtgärder för att installera/avinstallera stöds inte på Wear."
+ "Välj vad du vill ge <b>%1$s</b> åtkomst till"
+ "<b>%1$s</b> har uppdaterats. Välj vad du vill ge appen åtkomst till."
+ "Avbryt"
+ "Fortsätt"
+ "Nya behörigheter"
+ "Nuvarande behörighet"
+ "Provkör appen …"
+ "Okänd"
+ "Av säkerhetsskäl får okända appar från den här källan inte installeras av surfplattan."
+ "Av säkerhetsskäl får okända appar från den här källan inte installeras av TV:n."
+ "Av säkerhetsskäl får okända appar från den här källan inte installeras av mobilen."
+ "Din mobil och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på mobilen och för dataförlust som kan uppstå vid användning av denna app."
+ "Din surfplatta och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på surfplattan och för dataförlust som kan uppstå vid användning av denna app."
+ "Din TV och personliga data är mer sårbara för attacker från okända appar. Genom att installera denna app bekräftar du att du är ansvarig för eventuella skador på TV:n och för dataförlust som kan uppstå vid användning av denna app."
+ "Fortsätt"
+ "Inställningar"
+ "Wear-appar installeras/avinstalleras"
+
diff --git a/packages/PackageInstaller/res/values-sw-television/strings.xml b/packages/PackageInstaller/res/values-sw-television/strings.xml
new file mode 100644
index 0000000000000..25fd6960ad341
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Hapana na usiulize tena"
+ "Unaweza kubadilisha hatua hii baadaye kwenye Mipangilio na Programu"
+ "%1$s / %2$s"
+ "Onyesha programu za mfumo"
+ "Ruhusa za programu"
+ "Ruhusa za programu"
+ "Ruhusa za %1$s"
+ "Ruhusa za ziada"
+ "Ruhusa za %1$s"
+
diff --git a/packages/PackageInstaller/res/values-sw-watch/strings.xml b/packages/PackageInstaller/res/values-sw-watch/strings.xml
new file mode 100644
index 0000000000000..30d68e54af570
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Hapana, usiulize tena"
+ "%1$s / %2$s"
+ "Onyesha programu za mfumo"
+ "Haiwezi kubadilishwa"
+ "Ndiyo"
+ "Ghairi"
+
diff --git a/packages/PackageInstaller/res/values-sw/strings.xml b/packages/PackageInstaller/res/values-sw/strings.xml
new file mode 100644
index 0000000000000..c4319839eb9ef
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Kisakinishaji cha furushi"
+ "Inayofuata"
+ "Sakinisha"
+ "Nimemaliza"
+ "Ghairi"
+ "inawekwa..."
+ "Inasakinisha %1$s…"
+ "Programu imewekwa."
+ "Je, ungependa kuiweka programu hii? Itaweza:"
+ "Je, ungependa kuiweka programu hii? Haihitaji idhini ya kufikia kitu chochote."
+ "Je, unataka kuweka sasisho katika programu hii? Data yako iliyopo haitapotea. Programu iliyosasishwa itaweza:"
+ "Je, unataka kuweka sasisho la programu hii iliyojengewa ndani? Data yako iliyopo haitapotea. Programu iliyosasishwa itaweza:"
+ "Je, unataka kuweka sasisho la programu hii? Data yako iliyopo haitapotea. Haihitaji idhini yoyote maalum ya kufikia."
+ "Je, unataka kuweka sasisho la programu hii? Data yako iliyopo haitapotea. Haihitaji idhini yoyote maalum ya kufikia."
+ "Programu haikusakinishwa."
+ "Kifurushi kimezuiwa kisisakinishwe."
+ "Programu haikusakinishwa kwa sababu haiafikiani na kifurushi kingine kilichopo."
+ "Programu haikusakinishwa kwa sababu haioani na kompyuta kibao yako."
+ "Programu hii haioani na runinga yako."
+ "Programu haikusakinishwa kwa sababu haioani na simu yako."
+ "Programu haikusakinishwa kwa sababu inaonekana kuwa kifurushi si sahihi."
+ "%1$s haikuweza kusakinishwa kwenye kompyuta yako kibao."
+ "%1$s haikuweza kusakinishwa kwenye runinga yako."
+ "%1$s haikuweza kusakinishwa kwenye simu yako."
+ "Fungua"
+ "Msimamizi wako haruhusu usakinishaji wa programu zinazopatikana kutoka vyanzo visivyojulikana"
+ "Mtumiaji huyu hana idhini ya kusakinisha programu ambazo hazijulikani"
+ "Mtumiaji huyu haruhusiwi kusakinisha programu"
+ "Sawa"
+ "Dhibiti programu"
+ "Nafasi imeisha"
+ "%1$s haingeweza kusakinishwa. Wezesha nafasi kiasi na ujaribu tena."
+ "Programu haikupatikana"
+ "Programu haikupatikana katika orodha ya programu zilizosakinishwa."
+ "Hairuhusiwi"
+ "Mtumiaji wa sasa hana ruhusa ya kuondoa kipengee hiki."
+ "Hitilafu"
+ "Imeshindwa kuondoa programu."
+ "Sanidua programu"
+ "Sanidua kisasisho"
+ "%1$s ni sehemu ya programu ifuatayo:"
+ "Unataka kusanidua programu hii?"
+ "Je, unataka kusanidua programu hii kwa ""watumiaji"" wote? Programu na data yake zitaondolewa kutoka kwa ""watumiaji"" kwenye kifaa."
+ "Je, unataka kuondoa programu hii kwa mtumiaji %1$s?"
+ "Ungependa kubadilisha programu hii na toleo la kiwanda? Data yote itaondolewa."
+ "Ungependa kubadilisha programu hii na toleo la kiwanda? Data yote itaondolewa. Hatua hii itaathiri watumiaji wote wa kifaa hiki, ikiwa ni pamoja na wale walio na wasifu za kazini."
+ "Mara ambazo programu inaondolewa sasa"
+ "Mara ambazo programu haikuondolewa"
+ "Inasanidua..."
+ "Inaondoa %1$s…"
+ "Imesaniduliwa."
+ "Imeondoa %1$s"
+ "Kusanidua hakukufaulu."
+ "Imeshindwa kuondoa %1$s."
+ "Imeshindwa kuondoa programu inayotumika ya msimamizi wa kifaa"
+ "Imeshindwa kuondoa programu inayotumika ya msimamizi wa kifaa cha %1$s"
+ "Baadhi ya wasifu au watumiaji wanahitaji programu, kwa hivyo haijaondolewa kwa wengine"
+ "Programu hii inahitajika kwa wasifu wako kwa hivyo haiwezi kuondolewa."
+ "Programu hii inahitajika na msimamizi wako wa kifaa na haiwezi kuondolewa."
+ "Dhibiti programu za msimamizi wa kifaa"
+ "Dhibiti watumiaji"
+ "%1$s haingeweza kusaniduliwa."
+ "Kulikuwa na tatizo la kuchanganua furushi."
+ "Mpya"
+ "Zote"
+ "Faragha"
+ "Kufikia Kifaa"
+ "Sasisho hili halihitaji vibali vipya."
+ "Hapana"
+ "Maelezo zaidi"
+ "Kataa"
+ "%1$s kati ya %2$s"
+ "Ungependa kuruhusu <b>%1$s</b> %2$s?"
+ "Ungependa kuruhusu <b>%1$s</b> itekeleze %2$s kila wakati?"
+ "Wakati unatumia programu tu"
+ "Kila wakati"
+ "Kataa na usiulize tena"
+ "%1$d zimezimwa"
+ "zimezimwa zote"
+ "hakuna zilizozimwa"
+ "Ndiyo"
+ "Programu"
+ "Ruhusa za programu"
+ "Usiulize tena"
+ "Hakuna ruhusa"
+ "Ruhusa za ziada"
+ "Fungua maelezo ya programu"
+
+ %1$d zaidi
+ %1$d zaidi
+
+ "Programu hii iliundwa kwa ajili ya toleo la zamani la Android. Kuinyima ruhusa kunaweza kusababisha iache kutenda kazi kama ilivyokusudiwa."
+ "kutekeleza kitendo kisichojulikana"
+ "Inaruhusu programu %1$d kati ya %2$d"
+ "Onyesha mfumo"
+ "Ficha mfumo"
+ "Hakuna programu"
+ "Mipangilio ya Mahali"
+ "%1$s hutoa huduma za mahali kwenye kifaa hiki. Idhini ya kufikia mahali inaweza kurekebishwa katika mipangilio ya mahali."
+ "Usipokubali ruhusa hii, huenda vipengele vya msingi vya kifaa chako havitafanya kazi kama ilivyokusudiwa."
+ "Hutekelezwa na sera"
+ "Ufikiaji wa chinichini umezimwa na sera"
+ "Ufikiaji wa chinichini umewashwa na sera"
+ "Ufikiaji wa hadharani umewashwa na sera"
+ "Imedhibitiwa na msimamizi"
+
+
+
+ "Kila wakati"
+ "Wakati unatumia programu tu"
+ "Kamwe"
+ "Inapakia…"
+ "Ruhusa zote"
+ "Uwezo mwingine wa programu"
+ "Ombi la idhini"
+ "Imetambua tangazo lililowekelewa juu ya skrini"
+ "Ili kubadilisha mpangilio huu wa ruhusa, ni lazima kwanza uzime tangazo lililowekelewa juu ya skrini kwenye Mipangilio na Programu"
+ "Fungua mipangilio"
+ "Android Wear"
+ "Huduma ya Android Wear haiwezi kutekeleza vitendo vya Kusakinisha au Kuondoa vipengee."
+ "Chagua vipengee ambavyo unaruhusu <b>%1$s</b> ifikie"
+ "<b>%1$s</b> imesasishwa. Chagua vipengee unavyoruhusu programu hii ifikie."
+ "Ghairi"
+ "Endelea"
+ "Ruhusa mpya"
+ "Ruhusa zilizopo"
+ "Inatayarisha programu..."
+ "Isiyojulikana"
+ "Kwa sababu ya usalama wako, kompyuta yako kibao haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."
+ "Kwa sababu ya usalama wako, TV yako haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."
+ "Kwa sababu ya usalama wako, simu yako haina ruhusa ya kusakinisha programu ambazo hazijulikani, kutoka kwenye chanzo hiki."
+ "Data yako ya binafsi na ya simu yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye simu yako au kupotea kwa data kutokana na matumizi yake."
+ "Data yako ya binafsi na ya kompyuta yako kibao inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye kompyuta yako kibao au kupotea kwa data kutokana na matumizi yake."
+ "Data yako ya binafsi na ya televisheni yako inaweza kuathiriwa na programu ambazo hazijulikani. Kwa kusakinisha programu hii, unakubali kuwajibika kutokana na uharibifu wowote kwenye televisheni yako au kupotea kwa data kutokana na matumizi yake."
+ "Endelea"
+ "Mipangilio"
+ "Inasakinisha/inaondoa programu za Android Wear"
+
diff --git a/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml b/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml
new file mode 100644
index 0000000000000..84072a28ade5b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw180dp-notround-watch/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ 48dp
+
diff --git a/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml b/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml
new file mode 100644
index 0000000000000..d896138bd9994
--- /dev/null
+++ b/packages/PackageInstaller/res/values-sw210dp-round-watch/dimens.xml
@@ -0,0 +1,19 @@
+
+
+
+
+ 54dp
+
diff --git a/packages/PackageInstaller/res/values-ta-television/strings.xml b/packages/PackageInstaller/res/values-ta-television/strings.xml
new file mode 100644
index 0000000000000..786bbe28faa23
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "நிராகரி, மீண்டும் கேட்காதே"
+ "அமைப்புகள் > பயன்பாடுகள் என்பதில் பிறகு மாற்றலாம்"
+ "%1$s / %2$s"
+ "முறைமைப் பயன்பாடுகளைக் காட்டு"
+ "பயன்பாட்டு அனுமதிகள்"
+ "பயன்பாட்டு அனுமதிகள்"
+ "%1$s அனுமதிகள்"
+ "கூடுதல் அனுமதிகள்"
+ "%1$s அனுமதிகள்"
+
diff --git a/packages/PackageInstaller/res/values-ta-watch/strings.xml b/packages/PackageInstaller/res/values-ta-watch/strings.xml
new file mode 100644
index 0000000000000..23dab29d2ecac
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "நிராகரி, மீண்டும் கேட்காதே"
+ "%1$s / %2$s"
+ "முறைமைப் பயன்பாடுகளைக் காட்டு"
+ "மாற்ற முடியாது"
+ "ஆம்"
+ "ரத்துசெய்"
+
diff --git a/packages/PackageInstaller/res/values-ta/strings.xml b/packages/PackageInstaller/res/values-ta/strings.xml
new file mode 100644
index 0000000000000..366f6d9eb5cb0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ta/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "பேக்கேஜ் இன்ஸ்டாலர்"
+ "அடுத்து"
+ "நிறுவு"
+ "முடிந்தது"
+ "ரத்துசெய்"
+ "நிறுவுகிறது…"
+ "%1$sஐ நிறுவுகிறது…"
+ "பயன்பாடு நிறுவப்பட்டது."
+ "இந்தப் பயன்பாட்டை நிறுவ விரும்புகிறீர்களா? அது இதற்கான அணுகலைப் பெறும்:"
+ "இந்தப் பயன்பாட்டை நிறுவ விரும்புகிறீர்களா? இதற்கு எந்தத் தனிப்பட்ட அணுகலும் தேவையில்லை."
+ "முன்பே உள்ள இந்தப் பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். புதுப்பிக்கப்பட்ட பயன்பாடு இதற்கான அணுகலைப் பெறும்:"
+ "உள்ளமைக்கப்பட்ட பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். புதுப்பிக்கப்பட்ட பயன்பாடு இதற்கான அணுகலைப் பெறும்:"
+ "முன்பே உள்ள இந்தப் பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்கமாட்டீர்கள். இதற்குத் தனிப்பட்ட அணுகல் எதுவும் தேவையில்லை."
+ "உள்ளமைக்கப்பட்ட பயன்பாட்டில் புதுப்பிப்பை நிறுவ விரும்புகிறீர்களா? ஏற்கனவே உள்ள உங்கள் தரவை இழக்க மாட்டீர்கள். இதற்குத் தனிப்பட்ட அணுகல் எதுவும் தேவையில்லை."
+ "பயன்பாடு நிறுவப்படவில்லை."
+ "இந்தத் தொகுப்பு நிறுவுவதிலிருந்து தடுக்கப்பட்டது."
+ "தொகுப்பானது தற்போதுள்ள தொகுப்புடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."
+ "உங்கள் டேப்லெட்டுடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."
+ "உங்கள் டிவியுடன் இந்தப் பயன்பாடு இணங்கவில்லை."
+ "உங்கள் மொபைலுடன் இணக்கமற்றதாக உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."
+ "தொகுப்பு தவறானது போல் உள்ளதால், பயன்பாடு நிறுவப்படவில்லை."
+ "%1$s பயன்பாட்டை உங்கள் டேப்லெட்டில் நிறுவ முடியாது."
+ "உங்கள் டிவியில் %1$sஐ நிறுவ முடியவில்லை."
+ "%1$s பயன்பாட்டை உங்கள் மொபைலில் நிறுவ முடியாது."
+ "திற"
+ "அறியப்படாத மூலங்களிலிருந்து பெற்ற பயன்பாடுகளை நிறுவ, உங்கள் நிர்வாகி அனுமதிக்கவில்லை"
+ "அறியப்படாத பயன்பாடுகளை, இந்தப் பயனர் நிறுவ முடியாது"
+ "பயன்பாடுகளை நிறுவ, இந்தப் பயனருக்கு அனுமதியில்லை"
+ "சரி"
+ "பயன்பாடுகளை நிர்வகிக்கவும்"
+ "இடம் இல்லை"
+ "%1$s பயன்பாட்டை நிறுவ முடியாது. சில இடத்தைக் காலி செய்து மீண்டும் முயற்சிக்கவும்."
+ "பயன்பாடு கண்டறியப்படவில்லை"
+ "நிறுவிய பயன்பாடுகளின் பட்டியலில் பயன்பாடு இல்லை."
+ "அனுமதிக்கப்படவில்லை"
+ "இதை நிறுவல் நீக்குவதற்கு, தற்போதைய பயனர் அனுமதிக்கப்படவில்லை."
+ "பிழை"
+ "பயன்பாட்டை நிறுவல் நீக்க முடியவில்லை."
+ "பயன்பாட்டை நிறுவல் நீக்கு"
+ "புதுப்பிப்பை நிறுவல் நீக்கு"
+ "%1$s ஆனது பின்வரும் பயன்பாட்டின் பகுதியாகும்:"
+ "இந்தப் பயன்பாட்டை நிறுவல் நீக்க விரும்புகிறீர்களா?"
+ "இந்தப் பயன்பாட்டை ""எல்லா"" பயனர்களுக்கும் நிறுவல் நீக்க விரும்புகிறீர்களா? பயன்பாடும், அதன் தரவும் சாதனத்தில் உள்ள ""எல்லா"" பயனர்களிடமிருந்தும் அகற்றப்படும்."
+ "%1$s பயனருக்கான இந்தப் பயன்பாட்டை நிறுவல்நீக்க விரும்புகிறீர்களா?"
+ "ஆரம்பநிலைப் பதிப்பாக இந்தப் பயன்பாட்டை மாற்றியமைக்கவா? எல்லா தரவும் அகற்றப்படும்."
+ "ஆரம்பநிலைப் பதிப்பாக இந்தப் பயன்பாட்டை மாற்றியமைக்கவா? எல்லா தரவும் அகற்றப்படும். பணிச் சுயவிவரங்களுடன் உள்ளவர்கள் உட்பட இந்தச் சாதனத்தின் எல்லா பயனர்களையும் இது பாதிக்கும்."
+ "இயக்கத்திலுள்ள நிறுவல் நீக்கங்கள்"
+ "தோல்வியடைந்த நிறுவல் நீக்கங்கள்"
+ "நிறுவலை நீக்குகிறது…"
+ "%1$sஐ நிறுவல் நீக்குகிறது…"
+ "நிறுவல் நீக்குவது முடிந்தது."
+ "%1$s நிறுவல் நீக்கப்பட்டது"
+ "நிறுவல் நீக்குவதில் தோல்வி."
+ "%1$sஐ நிறுவல் நீக்க முடியவில்லை."
+ "செயலில் உள்ள சாதன நிர்வாகிப் பயன்பாட்டை நிறுவல் நீக்க முடியாது"
+ "%1$sக்கான செயலில் உள்ள சாதன நிர்வாகிப் பயன்பாட்டை நிறுவல் நீக்க முடியாது"
+ "இது சில பயனர்கள்/சுயவிவரங்களுக்குத் தேவைப்படுவதால், நிறுவல்நீக்க முடியாது, பிறருக்கு நிறுவல் நீக்கப்பட்டது"
+ "உங்கள் சுயவிவரத்திற்கு இந்தப் பயன்பாடு தேவைப்படுவதால், அதை நிறுவல்நீக்க முடியாது, பிறருக்கு நிறுவல் நீக்கப்பட்டது."
+ "சாதன நிர்வாகிக்கு இந்தப் பயன்பாடு தேவைப்படுவதால், நிறுவல்நீக்க முடியாது."
+ "சாதன நிர்வாகிப் பயன்பாடுகளை நிர்வகி"
+ "பயனர்களை நிர்வகி"
+ "%1$s பயன்பாட்டை நிறுவல் நீக்க முடியாது."
+ "தொகுப்பைக் குறியீட்டு ஆய்வு செய்வதில் சிக்கல் ஏற்பட்டது."
+ "புதிது"
+ "எல்லாம்"
+ "தனியுரிமை"
+ "சாதன அணுகல்"
+ "இந்தப் புதுப்பிப்பிற்குப் புதிய அனுமதிகள் எதுவும் தேவையில்லை."
+ "நிராகரி"
+ "மேலும் தகவல்"
+ "பரவாயில்லை, நிராகரி"
+ "%1$s / %2$s"
+ "செயலைச் செய்ய %2$s, <b>%1$s</b>ஐ அனுமதிக்கவா?"
+ "%2$sஐச் செய்ய <b>%1$s</b> ஆப்ஸை எப்போதும் அனுமதிக்கவா?"
+ "ஆப்ஸைப் பயன்படுத்தும்போது மட்டும் அனுமதி"
+ "எப்போதும் அனுமதி"
+ "நிராகரி, மீண்டும் கேட்காதே"
+ "%1$d முடக்கப்பட்டன"
+ "எல்லாம் முடக்கப்பட்டன"
+ "எதுவும் முடக்கப்படவில்லை"
+ "அனுமதி"
+ "ஆப்ஸ்"
+ "பயன்பாட்டு அனுமதிகள்"
+ "மீண்டும் கேட்காதே"
+ "அனுமதிகள் இல்லை"
+ "கூடுதல் அனுமதிகள்"
+ "பயன்பாட்டுத் தகவலைத் திற"
+
+ மேலும் %1$d
+ மேலும் %1$d
+
+ "இந்தப் பயன்பாடு Android இன் பழைய பதிப்புக்காக வடிவமைக்கப்பட்டது. அனுமதியை மறுத்தால் அது சரியாக செயல்படாமல் போகலாம்."
+ "அறியாத செயலைச் செயல்படுத்தும்"
+ "அனுமதிக்கப்பட்ட ஆப்ஸ்: %1$d/%2$d"
+ "எல்லாம் காட்டு"
+ "முறைமையை மறை"
+ "பயன்பாடுகள் இல்லை"
+ "இருப்பிட அமைப்புகள்"
+ "இந்தச் சாதனத்திற்கான இருப்பிடச் சேவைகளின் வழங்குநர் %1$s ஆகும். இருப்பிட அமைப்புகளிலிருந்து இருப்பிட அணுகலை மாற்றலாம்."
+ "இந்த அனுமதியை நிராகரித்தால், உங்கள் சாதனத்தின் அடிப்படை அம்சங்கள் சரியாகச் செயல்படாமல் போகலாம்."
+ "கொள்கையின் படி செயல்படுத்தப்பட்டது"
+ "கொள்கையின்படி பின்புல அணுகல் முடக்கப்பட்டது"
+ "கொள்கையின்படி பின்புல அணுகல் இயக்கப்பட்டது"
+ "கொள்கையின்படி முன்புல அணுகல் இயக்கப்பட்டது"
+ "நிர்வாகி கட்டுப்படுத்துகிறார்"
+
+
+
+ "எப்போதும்"
+ "ஆப்ஸை உபயோகிக்கும்போது மட்டும்"
+ "ஒருபோதும் வேண்டாம்"
+ "ஏற்றுகிறது..."
+ "எல்லா அனுமதிகளும்"
+ "பயன்பாட்டின் பிற திறன்கள்"
+ "அனுமதி கோரிக்கை"
+ "திரையின் மேலே செயல்படும் பயன்பாடுகள் கண்டறியப்பட்டன"
+ "இந்த அனுமதியை மாற்ற, அமைப்புகள் > பயன்பாடுகள் என்பதற்குச் சென்று, திரையின் மேலே செயல்படும் பயன்பாடுகளை முதலில் முடக்கவும்"
+ "அமைப்புகளைத் திற"
+ "Android Wear"
+ "Wear இல் நிறுவுதல்/நிறுவல் நீக்குதலுக்கு ஆதரவில்லை."
+ "<b>%1$s</b> எவற்றை அணுகலாம் என்பதைத் தேர்வுசெய்யவும்"
+ "<b>%1$s</b> புதுப்பிக்கப்பட்டது. இந்தப் பயன்பாடு எவற்றை அணுகலாம் என்பதைத் தேர்வுசெய்யவும்."
+ "ரத்துசெய்"
+ "தொடர்க"
+ "புதிய அனுமதிகள்"
+ "தற்போதைய அனுமதிகள்"
+ "பயன்பாடு தயாராகிறது…"
+ "தெரியாதது"
+ "உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் டேப்லெட்டில் நிறுவ முடியாது."
+ "உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் டிவியில் நிறுவ முடியாது."
+ "உங்கள் பாதுகாப்பிற்காக, இந்த மூலத்திலிருந்து அறியப்படாத பயன்பாடுகளை உங்கள் மொபைலில் நிறுவ முடியாது."
+ "அறியப்படாத பயன்பாடுகள், உங்கள் மொபைலையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் மொபைலுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."
+ "அறியப்படாத பயன்பாடுகள், உங்கள் டேப்லெட்டையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டேப்லெட்டுக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."
+ "அறியப்படாத பயன்பாடுகள், உங்கள் டிவியையும் தனிப்பட்ட தரவையும் அதிகம் பாதிக்கக்கூடும். இந்தப் பயன்பாட்டை நிறுவுவதால், அவற்றைப் பயன்படுத்தும் போது உங்கள் டிவிக்கு ஏதேனும் சேதம் ஏற்பட்டாலோ அல்லது தரவை இழந்தாலோ, அதற்கு நீங்கள்தான் பொறுப்பாவீர்கள் என்பதை ஏற்கிறீர்கள்."
+ "தொடர்க"
+ "அமைப்புகள்"
+ "வியர் ஆப்ஸை நிறுவுதல்/நிறுவல் நீக்குதல்"
+
diff --git a/packages/PackageInstaller/res/values-te-television/strings.xml b/packages/PackageInstaller/res/values-te-television/strings.xml
new file mode 100644
index 0000000000000..51526b7e2bb46
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "నిరాకరిస్తున్నాను, మళ్లీ అడగవద్దు"
+ "మీరు దీన్ని తర్వాత సెట్టింగ్లు > అనువర్తనాల్లో మార్చవచ్చు"
+ "%1$s / %2$s"
+ "సిస్టమ్ అనువర్తనాలను చూపు"
+ "యాప్ అనుమతులు"
+ "యాప్ అనుమతులు"
+ "%1$s అనుమతులు"
+ "అదనపు అనుమతులు"
+ "%1$s అనుమతులు"
+
diff --git a/packages/PackageInstaller/res/values-te-watch/strings.xml b/packages/PackageInstaller/res/values-te-watch/strings.xml
new file mode 100644
index 0000000000000..d97e9707bf4bc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "నిరాకరిస్తున్నాను,ఇక అడగవద్దు"
+ "%1$s / %2$s"
+ "సిస్టమ్ అనువర్తనాలను చూపు"
+ "మార్చడం సాధ్యపడదు"
+ "అవును"
+ "రద్దు చేయి"
+
diff --git a/packages/PackageInstaller/res/values-te/strings.xml b/packages/PackageInstaller/res/values-te/strings.xml
new file mode 100644
index 0000000000000..b612385acc8a1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-te/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "ప్యాకేజీ ఇన్స్టాలర్"
+ "తర్వాత"
+ "ఇన్స్టాల్ చేయండి"
+ "పూర్తయింది"
+ "రద్దు చేయి"
+ "ఇన్స్టాల్ చేస్తోంది…"
+ "%1$sని ఇన్స్టాల్ చేస్తోంది…"
+ "యాప్ ఇన్స్టాల్ చేయబడింది."
+ "మీరు ఈ అనువర్తనాన్ని ఇన్స్టాల్ చేయాలనుకుంటున్నారా? ఇది వీటికి ప్రాప్యతను పొందుతుంది:"
+ "మీరు ఈ యాప్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా? దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."
+ "మీరు ఈ ప్రస్తుత యాప్నకు అప్డేట్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. అప్డేట్ చేసిన యాప్ వీటికి యాక్సెస్ను పొందుతుంది:"
+ "మీరు ఈ అంతర్నిర్మిత యాప్నకు అప్డేట్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. అప్డేట్ చేసిన యాప్ వీటికి యాక్సెస్ పొందుతుంది:"
+ "మీరు ఈ ప్రస్తుత యాప్కు అప్డేట్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."
+ "మీరు ఈ అంతర్నిర్మిత యాప్కు అప్డేట్ను ఇన్స్టాల్ చేయాలనుకుంటున్నారా? మీ ప్రస్తుత డేటాను కోల్పోవడం సంభవించదు. దీనికి ఎటువంటి ప్రత్యేక యాక్సెస్ అవసరం లేదు."
+ "యాప్ ఇన్స్టాల్ చేయబడలేదు."
+ "ప్యాకేజీ ఇన్స్టాల్ కాకుండా బ్లాక్ చేయబడింది."
+ "ప్యాకేజీ ఇప్పటికే ఉన్న ప్యాకేజీకి వైరుధ్యంగా ఉన్నందున యాప్ ఇన్స్టాల్ చేయబడలేదు."
+ "యాప్ మీ టాబ్లెట్కు అనుకూలంగా లేని కారణంగా ఇన్స్టాల్ చేయబడలేదు."
+ "ఈ యాప్ మీ టీవీకి అనుకూలంగా లేదు."
+ "యాప్ మీ ఫోన్కు అనుకూలంగా లేని కారణంగా ఇన్స్టాల్ చేయబడలేదు."
+ "ప్యాకేజీ చెల్లుబాటు కాని విధంగా ఉన్నందున యాప్ ఇన్స్టాల్ చేయబడలేదు."
+ "%1$sను మీ టాబ్లెట్లో ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."
+ "%1$sని మీ టీవీలో ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."
+ "%1$sను మీ ఫోన్లో ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."
+ "తెరవండి"
+ "మీ నిర్వాహకులు తెలియని మూలాల నుండి పొందిన అనువర్తనాల ఇన్స్టాలేషన్ను అనుమతించరు"
+ "తెలియని అనువర్తనాలను ఈ వినియోగదారు ఇన్స్టాల్ చేయలేరు"
+ "యాప్లను ఇన్స్టాల్ చేయడానికి ఈ వినియోగదారుకు అనుమతి లేదు"
+ "సరే"
+ "అనువర్తనాలను నిర్వహించండి"
+ "ఖాళీ లేదు"
+ "%1$sని ఇన్స్టాల్ చేయడం సాధ్యపడలేదు. కొంత స్థలాన్ని ఖాళీ చేసి మళ్లీ ప్రయత్నించండి."
+ "యాప్ కనుగొనబడలేదు"
+ "ఇన్స్టాల్ చేసిన యాప్ల జాబితాలో యాప్ కనుగొనబడలేదు."
+ "అనుమతించబడలేదు"
+ "ప్రస్తుత వినియోగదారు ఈ అన్ఇన్స్టాలేషన్ చేసేందుకు అనుమతి లేదు."
+ "ఎర్రర్"
+ "అనువర్తనాన్ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడదు."
+ "యాప్ను అన్ఇన్స్టాల్ చేయండి"
+ "నవీకరణను అన్ఇన్స్టాల్ చేయండి"
+ "%1$s అనేది క్రింది యాప్లో ఒక భాగం:"
+ "మీరు ఈ యాప్ను అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"
+ "మీరు ఈ యాప్ను ""మొత్తం"" వినియోగదారులకు అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా? యాప్ మరియు దీని డేటా డివైజ్లోని ""మొత్తం"" వినియోగదారుల నుండి తీసివేయబడుతుంది."
+ "మీరు వినియోగదారు %1$s కోసం ఈ అనువర్తనాన్ని అన్ఇన్స్టాల్ చేయాలనుకుంటున్నారా?"
+ "ఈ అనువర్తనాన్ని ఫ్యాక్టరీ సంస్కరణతో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది."
+ "ఈ అనువర్తనాన్ని ఫ్యాక్టరీ సంస్కరణతో భర్తీ చేయాలా? మొత్తం డేటా తీసివేయబడుతుంది. దీని ప్రభావం కార్యాలయ ప్రొఫైల్లు కలిగి ఉన్నవారితో సహా ఈ పరికర వినియోగదారులందరిపై ఉంటుంది."
+ "అమల్లో ఉన్న అన్ఇన్స్టాల్లు"
+ "విఫలమైన అన్ఇన్స్టాల్లు"
+ "అన్ఇన్స్టాల్ చేస్తోంది…"
+ "%1$sని అన్ఇన్స్టాల్ చేస్తోంది…"
+ "అన్ఇన్స్టాల్ చేయడం ముగిసింది."
+ "అన్ఇన్స్టాల్ చేసిన %1$s"
+ "అన్ఇన్స్టాల్ చేయడం విజయవంతం కాలేదు."
+ "%1$s అన్ఇన్స్టాల్ చేయడంలో విఫలమైంది."
+ "సక్రియ పరికర నిర్వాహక అనువర్తనాన్ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడదు"
+ "%1$s కోసం సక్రియ పరికర నిర్వాహక అనువర్తనాన్ని అన్ఇన్స్టాల్ చేయడం సాధ్యపడదు"
+ "ఈ యాప్ కొందరు వినియోగదారులకు లేదా కొన్ని ప్రొఫైల్లకు అవసరం, ఇతరులకు అన్ఇన్స్టాల్ చేయబడింది"
+ "మీ ప్రొఫైల్ కోసం ఈ యాప్ అవసరం మరియు దీన్ని అన్ఇన్స్టాల్ చేయలేరు."
+ "మీ డివైజ్ నిర్వాహకుడికి ఈ యాప్ అవసరం, అందువల్ల దీన్ని అన్ఇన్స్టాల్ చేయడం కుదరదు."
+ "పరికర నిర్వాహక అనువర్తనాలను నిర్వహించు"
+ "వినియోగదారులను నిర్వహించు"
+ "%1$sని అన్ఇన్స్టాల్ చేయడం సాధ్యపడలేదు."
+ "ప్యాకేజీని అన్వయించడంలో సమస్య ఏర్పడింది."
+ "కొత్తవి"
+ "అన్నీ"
+ "గోప్యత"
+ "పరికరం యాక్సెస్"
+ "ఈ నవీకరణకు కొత్త అనుమతులు అవసరం లేదు."
+ "తిరస్కరించు"
+ "మరింత సమాచారం"
+ "ఏదేమైనా నిరాకరించు"
+ "%2$sలో %1$s"
+ "<b>%1$s</b>ని %2$s అనుమతించాలా?"
+ "%2$s చేయడానికి <b>%1$s</b>ను ఎల్లప్పుడూ అనుమతించాలా?"
+ "యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే"
+ "ఎల్లప్పుడూ"
+ "నిరాకరించు, మళ్లీ అడగవద్దు"
+ "%1$d నిలిపివేయబడ్డాయి"
+ "అన్నీ నిలిపివేయబడ్డాయి"
+ "ఏవీ నిలిపివేయబడలేదు"
+ "అనుమతించు"
+ "యాప్లు"
+ "యాప్ అనుమతులు"
+ "మళ్లీ అడగవద్దు"
+ "అనుమతులు లేవు"
+ "అదనపు అనుమతులు"
+ "యాప్ సమాచారాన్ని తెరుస్తుంది"
+
+ మరో %1$d
+ మరో %1$d
+
+ "ఈ యాప్ పాత Android వెర్షన్ కోసం రూపొందించబడింది. అనుమతిని నిరాకరించినట్లయితే ఇది ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు."
+ "తెలియని చర్యను చేస్తుంది"
+ "%2$dలో %1$d యాప్లు అనుమతించబడ్డాయి"
+ "సిస్టమ్ను చూపు"
+ "సిస్టమ్ను దాచు"
+ "అనువర్తనాలు లేవు"
+ "స్థాన సెట్టింగ్లు"
+ "ఈ పరికరం కోసం స్థాన సేవల ప్రదాత %1$s. స్థాన సెట్టింగ్ల నుండి స్థాన ప్రాప్యతను సవరించవచ్చు."
+ "మీరు ఈ అనుమతిని నిరాకరిస్తే, మీ పరికర ప్రాథమిక లక్షణాలు ఇకపై ఉద్దేశించిన రీతిలో పని చేయకపోవచ్చు."
+ "విధానం ద్వారా అమలు చేయబడింది"
+ "విధానం ద్వారా నేపథ్య యాక్సెస్ నిలిపివేయబడింది"
+ "విధానం ద్వారా నేపథ్య యాక్సెస్ ప్రారంభించబడింది"
+ "విధానం ద్వారా ముందుభాగం యాక్సెస్ ప్రారంభించబడింది"
+ "నిర్వాహకుల నియంత్రణలో ఉంటాయి"
+
+
+
+ "ఎల్లప్పుడూ"
+ "యాప్ను ఉపయోగిస్తున్నప్పుడు మాత్రమే"
+ "ఎన్నడూ వద్దు"
+ "లోడ్ అవుతోంది..."
+ "అన్ని అనుమతులు"
+ "ఇతర అనువర్తన సామర్థ్యాలు"
+ "అనుమతి అభ్యర్థన"
+ "స్క్రీన్ అతివ్యాప్తి గుర్తించబడింది"
+ "ఈ అనుమతి సెట్టింగ్ను మార్చడానికి, మీరు ముందుగా సెట్టింగ్లు > అనువర్తనాల నుండి స్క్రీన్ అతివ్యాప్తిని ఆఫ్ చేయాలి"
+ "సెట్టింగ్లను తెరువు"
+ "Android వేర్"
+ "Wearలో ఇన్స్టాల్/అన్ఇన్స్టాల్ చర్యలకు మద్దతు లేదు."
+ "<b>%1$s</b> యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి"
+ "<b>%1$s</b> అప్డేట్ చేయబడింది. ఈ యాప్ యాక్సెస్ చేయడానికి అనుమతించాల్సిన వాటిని ఎంచుకోండి."
+ "రద్దు చేయి"
+ "కొనసాగించు"
+ "కొత్త అనుమతులు"
+ "ప్రస్తుత అనుమతులు"
+ "అనువర్తనాన్ని అందిస్తోంది…"
+ "తెలియదు"
+ "మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ టాబ్లెట్ అనుమతించబడదు."
+ "మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ TV అనుమతించబడదు."
+ "మీకు భద్రతను అందించడం కోసం, ఈ మూలం నుండి తెలియని యాప్లను ఇన్స్టాల్ చేయడానికి మీ ఫోన్ అనుమతించబడదు."
+ "మీ ఫోన్ మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం వలన మీ ఫోన్కు సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."
+ "మీ టాబ్లెట్ మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం ద్వారా మీ టాబ్లెట్కు సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."
+ "మీ TV మరియు వ్యక్తిగత డేటా తెలియని మూలాల్లోని అనువర్తనాల ద్వారా దాడికి గురి కావడానికి ఎక్కువ అవకాశం ఉంటుంది. ఈ అనువర్తనాన్ని ఇన్స్టాల్ చేయడం ద్వారా, ఈ అనువర్తనాన్ని ఉపయోగించడం ద్వారా మీ TVకి సంభవించే ఏదైనా నష్టానికి లేదా కోల్పోయే డేటాకి బాధ్యత వహించడానికి మీరు అంగీకరిస్తున్నారు."
+ "కొనసాగించు"
+ "సెట్టింగ్లు"
+ "వేర్ ఆప్లను ఇన్స్టాల్/అన్ఇన్స్టాల్ చేస్తోంది"
+
diff --git a/packages/PackageInstaller/res/values-television/colors.xml b/packages/PackageInstaller/res/values-television/colors.xml
new file mode 100644
index 0000000000000..6afe24830790b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/colors.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+ #FFF1F1F1
+ #88F1F1F1
+ #88F1F1F1
+ #FF111111
+ #77000000
+
+ #1f292d
+
+ #ff263238
+ @color/grant_permissions_white_text_alpha_100
+ @color/grant_permissions_white_text_alpha_100
+ @color/grant_permissions_white_text_alpha_70
+ @color/grant_permissions_white_text_alpha_70
+ @color/grant_permissions_white_text_alpha_100
+ #26eeeeee
+
+ @color/off_white
+ #b2eeeeee
+
+ #ffeeeeee
+
diff --git a/packages/PackageInstaller/res/values-television/dimens.xml b/packages/PackageInstaller/res/values-television/dimens.xml
new file mode 100644
index 0000000000000..03038386e08df
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/dimens.xml
@@ -0,0 +1,43 @@
+
+
+
+
+ 64dp
+ 19dp
+
+ 3dp
+ 18dp
+ 8dp
+
+ 16dp
+ 52dp
+ 40dp
+ 41dp
+ 27dp
+
+ 24dp
+ 32dp
+
+ 304dp
+ 24dp
+ 18dp
+
+ 16dp
+ 16dp
+ 14dp
+ 15dp
+ 48dp
+
diff --git a/packages/PackageInstaller/res/values-television/strings.xml b/packages/PackageInstaller/res/values-television/strings.xml
new file mode 100644
index 0000000000000..c11d865cedd85
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/strings.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Deny and don\'t ask again
+
+
+ You can change this later in Settings > Apps
+
+
+
+ %1$s /
+ %2$s
+
+
+ Show system apps
+
+
+ App permissions
+ App permissions
+ %1$s permissions
+ Additional permissions
+ %1$s permissions
+
diff --git a/packages/PackageInstaller/res/values-television/styles.xml b/packages/PackageInstaller/res/values-television/styles.xml
new file mode 100644
index 0000000000000..5f712f7d134b5
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/styles.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values-television/themes.xml b/packages/PackageInstaller/res/values-television/themes.xml
new file mode 100644
index 0000000000000..f778fad25ccdf
--- /dev/null
+++ b/packages/PackageInstaller/res/values-television/themes.xml
@@ -0,0 +1,71 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values-th-television/strings.xml b/packages/PackageInstaller/res/values-th-television/strings.xml
new file mode 100644
index 0000000000000..042a6f11a270f
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "ปฏิเสธและไม่ต้องถามอีก"
+ "คุณสามารถเปลี่ยนได้ภายหลังในการตั้งค่า > แอป"
+ "%1$s/%2$s"
+ "แสดงแอประบบ"
+ "สิทธิ์ของแอป"
+ "สิทธิ์ของแอป"
+ "สิทธิ์เข้าถึง \"%1$s\""
+ "สิทธิ์เพิ่มเติม"
+ "สิทธิ์เข้าถึง \"%1$s\""
+
diff --git a/packages/PackageInstaller/res/values-th-watch/strings.xml b/packages/PackageInstaller/res/values-th-watch/strings.xml
new file mode 100644
index 0000000000000..05af1e84a3b28
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "ปฏิเสธ ไม่ต้องถามอีก"
+ "%1$s / %2$s"
+ "แสดงแอประบบ"
+ "ไม่สามารถเปลี่ยน"
+ "ใช่"
+ "ยกเลิก"
+
diff --git a/packages/PackageInstaller/res/values-th/strings.xml b/packages/PackageInstaller/res/values-th/strings.xml
new file mode 100644
index 0000000000000..b7735a73a698b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-th/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "โปรแกรมติดตั้งแพ็กเกจ"
+ "ถัดไป"
+ "ติดตั้ง"
+ "เสร็จสิ้น"
+ "ยกเลิก"
+ "กำลังติดตั้ง..."
+ "กำลังติดตั้ง %1$s…"
+ "ติดตั้งแอปพลิเคชันแล้ว"
+ "คุณต้องการติดตั้งแอปพลิเคชันนี้หรือไม่ แอปพลิเคชันจะเข้าถึง:"
+ "คุณต้องการจะติดตั้งแอปพลิเคชันนี้หรือไม่ แอปพลิเคชันไม่ต้องมีการเข้าถึงพิเศษใดๆ"
+ "คุณต้องการติดตั้งการอัปเดตของแอปพลิเคชันที่มีอยู่นี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหายไป แอปพลิเคชันที่อัปเดตแล้วจะเข้าถึง:"
+ "คุณต้องการจะติดตั้งการอัปเดตของแอปพลิเคชันในระบบนี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย แอปพลิเคชันที่อัปเดตแล้วจะเข้าถึง:"
+ "คุณต้องการติดตั้งการอัปเดตไปยังแอปพลิเคชันที่มีอยู่นี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย การอัปเดตนี้ไม่จำเป็นต้องใช้การเข้าถึงใดๆ เป็นพิเศษ"
+ "คุณต้องการติดตั้งการอัปเดตไปยังแอปพลิเคชันในตัวนี้หรือไม่ ข้อมูลที่มีอยู่ของคุณจะไม่สูญหาย การอัปเดตนี้ไม่จำเป็นต้องใช้การเข้าถึงใดๆ เป็นพิเศษ"
+ "ไม่ได้ติดตั้งแอปพลิเคชัน"
+ "มีการบล็อกแพ็กเกจไม่ให้ติดตั้ง"
+ "ไม่ได้ติดตั้งแอปเพราะแพ็กเกจขัดแย้งกับแพ็กเกจที่มีอยู่"
+ "ไม่ได้ติดตั้งแอปเพราะแอปใช้งานไม่ได้กับแท็บเล็ตของคุณ"
+ "แอปนี้ไม่สามารถใช้งานกับทีวีของคุณ"
+ "ไม่ได้ติดตั้งแอปเพราะแอปใช้งานไม่ได้กับโทรศัพท์ของคุณ"
+ "ไม่ได้ติดตั้งแอปเพราะดูเหมือนว่าแพ็กเกจจะไม่ถูกต้อง"
+ "ไม่สามารถติดตั้ง %1$s บนแท็บเล็ตของคุณ"
+ "%1$s ไม่สามารถติดตั้งบนทีวีได้"
+ "ไม่สามารถติดตั้ง %1$s บนโทรศัพท์ของคุณ"
+ "เปิด"
+ "ผู้ดูแลระบบของคุณไม่อนุญาตให้ติดตั้งแอปที่ได้มาจากแหล่งที่มาที่ไม่รู้จัก"
+ "ผู้ใช้รายนี้ไม่สามารถติดตั้งแอปที่ไม่รู้จัก"
+ "ผู้ใช้รายนี้ไม่ได้รับอนุญาตให้ติดตั้งแอป"
+ "ตกลง"
+ "จัดการแอปพลิเคชัน"
+ "ไม่มีพื้นที่"
+ "ติดตั้ง %1$s ไม่ได้ เพิ่มพื้นที่ว่างแล้วลองอีกครั้ง"
+ "ไม่พบแอปพลิเคชัน"
+ "ไม่พบแอปพลิเคชันนี้ในรายการแอปพลิเคชันที่ติดตั้งไว้"
+ "ไม่ได้รับอนุญาต"
+ "ผู้ใช้ปัจจุบันไม่ได้รับอนุญาตให้ทำการถอนการติดตั้งนี้"
+ "พบข้อผิดพลาด"
+ "ไม่สามารถถอนการติดตั้งแอป"
+ "ถอนการติดตั้งแอปพลิเคชัน"
+ "ถอนการติดตั้งการอัปเดต"
+ "%1$s เป็นส่วนหนึ่งของแอปพลิเคชันต่อไปนี้:"
+ "คุณต้องการถอนการติดตั้งแอปพลิเคชันนี้หรือไม่"
+ "คุณต้องการถอนการติดตั้งแอปพลิเคชันนี้สำหรับผู้ใช้""ทั้งหมด""หรือไม่ แอปพลิเคชันนี้และข้อมูลในแอปพลิเคชันจะถูกลบจากผู้ใช้""ทั้งหมด""ในอุปกรณ์"
+ "คุณต้องการถอนการติดตั้งแอปนี้สำหรับผู้ใช้ %1$s ไหม"
+ "แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก"
+ "แทนที่แอปนี้ด้วยเวอร์ชันเริ่มต้นไหม ระบบจะนำข้อมูลทั้งหมดออก วิธีนี้ส่งผลต่อผู้ใช้ทุกคนที่ใช้อุปกรณ์เครื่องนี้ รวมทั้งผู้ที่มีโปรไฟล์งาน"
+ "กำลังเรียกใช้การถอนการติดตั้ง"
+ "การถอนการติดตั้งที่ล้มเหลว"
+ "กำลังถอนการติดตั้ง..."
+ "กำลังถอนการติดตั้ง %1$s…"
+ "ถอนการติดตั้งเสร็จแล้ว"
+ "ถอนการติดตั้ง %1$s แล้ว"
+ "ถอนการติดตั้งไม่สำเร็จ"
+ "การถอนการติดตั้ง %1$s ไม่สำเร็จ"
+ "ไม่สามารถถอนการติดตั้งแอปผู้ดูแลระบบอุปกรณ์ที่มีการใช้งาน"
+ "ไม่สามารถถอนการติดตั้งแอปผู้ดูแลระบบอุปกรณ์ที่มีการใช้งานสำหรับ %1$s"
+ "แอปนี้จำเป็นสำหรับผู้ใช้หรือโปรไฟล์บางส่วน และถอนการติดตั้งไปแล้วสำหรับส่วนอื่น"
+ "แอปนี้จำเป็นสำหรับโปรไฟล์ของคุณและไม่สามารถถอนการติดตั้งได้"
+ "ผู้ดูแลระบบอุปกรณ์กำหนดให้ใช้แอปนี้ และไม่สามารถถอนการติดตั้งได้"
+ "จัดการแอปผู้ดูแลระบบอุปกรณ์"
+ "จัดการผู้ใช้"
+ "ไม่สามารถถอดการติดตั้ง %1$s"
+ "พบปัญหาในการแยกวิเคราะห์แพ็กเกจ"
+ "ใหม่"
+ "ทั้งหมด"
+ "ข้อมูลส่วนบุคคล"
+ "การเข้าถึงอุปกรณ์"
+ "การอัปเดตนี้ไม่จำเป็นต้องมีการอนุญาตใหม่"
+ "ปฏิเสธ"
+ "ข้อมูลเพิ่มเติม"
+ "ยืนยันการปฏิเสธ"
+ "%1$s จาก %2$s รายการ"
+ "อนุญาตให้ <b>%1$s</b> %2$sไหม"
+ "อนุญาตให้ <b>%1$s</b> %2$sทุกครั้งใช่ไหม"
+ "ขณะใช้แอปเท่านั้น"
+ "ตลอดเวลา"
+ "ปฏิเสธและไม่ต้องถามอีก"
+ "ปิดใช้ %1$d สิทธิ์"
+ "ปิดใช้สิทธิ์ทั้งหมด"
+ "ไม่มีการปิดใช้สิทธิ์"
+ "อนุญาต"
+ "แอป"
+ "สิทธิ์ของแอป"
+ "ไม่ต้องถามอีก"
+ "ไม่มีสิทธิ์"
+ "สิทธิ์เพิ่มเติม"
+ "เปิดข้อมูลแอป"
+
+ อีก %1$d รายการ
+ อีก %1$d รายการ
+
+ "แอปนี้ออกแบบมาเพื่อ Android เวอร์ชันเก่า การปฏิเสธสิทธิ์อาจทำให้แอปไม่ทำงานตามที่ต้องการอีกต่อไป"
+ "ดำเนินการทำงานที่ไม่รู้จัก"
+ "อนุญาตแล้ว %1$d จาก %2$d แอป"
+ "แสดงระบบ"
+ "ซ่อนระบบ"
+ "ไม่มีแอป"
+ "การตั้งค่าตำแหน่ง"
+ "%1$s เป็นผู้ให้บริการตำแหน่งสำหรับอุปกรณ์นี้ คุณสามารถแก้ไขสิทธิ์เข้าถึงตำแหน่งได้จากการตั้งค่าตำแหน่ง"
+ "หากคุณปฏิเสธสิทธิ์นี้ ฟีเจอร์พื้นฐานของอุปกรณ์อาจไม่ทำงานตามที่ควรจะเป็นอีกต่อไป"
+ "บังคับใช้โดยนโยบาย"
+ "ปิดใช้การเข้าถึงในเบื้องหลังโดยนโยบาย"
+ "เปิดใช้การเข้าถึงในเบื้องหลังโดยนโยบาย"
+ "เปิดใช้การเข้าถึงในเบื้องหน้าโดยนโยบาย"
+ "ผู้ดูแลระบบเป็นผู้ควบคุม"
+
+
+
+ "ตลอดเวลา"
+ "ขณะใช้แอปเท่านั้น"
+ "ไม่เลย"
+ "กำลังโหลด…"
+ "สิทธิ์ทั้งหมด"
+ "ความสามารถอื่นๆ ของแอป"
+ "คำขอสิทธิ์"
+ "ตรวจพบการวางซ้อนหน้าจอ"
+ "ในการเปลี่ยนแปลงการตั้งค่าสิทธิ์นี้ ก่อนอื่น คุณต้องปิดการวางซ้อนหน้าจอที่การตั้งค่า > แอป"
+ "เปิดการตั้งค่า"
+ "Android Wear"
+ "ไม่สามารถติดตั้ง/ถอนการติดตั้งบน Wear"
+ "โปรดเลือกข้อมูลที่อนุญาตให้ <b>%1$s</b> เข้าถึง"
+ "อัปเดต <b>%1$s</b> แล้ว โปรดเลือกข้อมูลที่อนุญาตให้แอปนี้เข้าถึง"
+ "ยกเลิก"
+ "ต่อไป"
+ "สิทธิ์ใหม่"
+ "สิทธิ์ปัจจุบัน"
+ "กำลังปรับสภาพแวดล้อมของแอป…"
+ "ไม่ทราบ"
+ "เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในแท็บเล็ต"
+ "เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในทีวี"
+ "เพื่อความปลอดภัย ไม่อนุญาตให้ติดตั้งแอปที่ไม่รู้จักจากแหล่งที่มานี้ในโทรศัพท์"
+ "โทรศัพท์และข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องโทรศัพท์หรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"
+ "แท็บเล็ตและข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องแท็บเล็ตหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"
+ "ทีวีและข้อมูลส่วนบุคคลของคุณมีความเสี่ยงมากขึ้นที่จะถูกโจมตีจากแอปที่ไม่รู้จัก การติดตั้งแอปนี้เป็นการยอมรับว่าคุณจะรับผิดชอบความเสียหายต่อเครื่องทีวีหรือการสูญเสียข้อมูลที่อาจเกิดจากการใช้งานแอปดังกล่าว"
+ "ต่อไป"
+ "การตั้งค่า"
+ "กำลังติดตั้ง/ถอนการติดตั้งแอป Wear"
+
diff --git a/packages/PackageInstaller/res/values-tl-television/strings.xml b/packages/PackageInstaller/res/values-tl-television/strings.xml
new file mode 100644
index 0000000000000..df5b98f95846a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Tanggihan at huwag nang tatanunging muli"
+ "Mababago mo ito sa ibang pagkakataon sa Mga Setting > Mga App"
+ "%1$s / %2$s"
+ "Ipakita ang mga app ng system"
+ "Mga pahintulot sa app"
+ "Mga pahintulot sa app"
+ "Mga pahintulot sa %1$s"
+ "Mga karagdagang pahintulot"
+ "Mga pahintulot sa %1$s"
+
diff --git a/packages/PackageInstaller/res/values-tl-watch/strings.xml b/packages/PackageInstaller/res/values-tl-watch/strings.xml
new file mode 100644
index 0000000000000..859745147cbba
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Tanggihan, huwag nang tatanunging muli"
+ "%1$s / %2$s"
+ "Ipakita ang mga app ng system"
+ "Hindi mababago"
+ "Oo"
+ "Kanselahin"
+
diff --git a/packages/PackageInstaller/res/values-tl/strings.xml b/packages/PackageInstaller/res/values-tl/strings.xml
new file mode 100644
index 0000000000000..05eba98f79ef4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tl/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Installer ng package"
+ "Susunod"
+ "Mag-install"
+ "Tapos na"
+ "Kanselahin"
+ "Nag-i-install…"
+ "Ini-install ang %1$s…"
+ "Na-install ang app."
+ "Nais mo bang i-install ang application na ito? Magkakaroon ito ng access sa:"
+ "Nais mo bang i-install ang application na ito? Hindi ito nangangailangan ng anumang espesyal na access."
+ "Nais mo bang mag-install ng update sa umiiral nang application na ito? Hindi mawawala ang iyong umiiral nang data. Magkakaroon ng access ang na-update na application sa:"
+ "Nais mo bang mag-install ng update sa built-in na application na ito? Hindi mawawala ang iyong umiiral na data. Magkakaroon ng access ang na-update na application sa:"
+ "Nais mo bang mag-install ng update sa umiiral na application na ito? Hindi mawawala ang iyong umiiral na data. Hindi ito nangangailangan ng anumang espesyal na access."
+ "Nais mo bang mag-install ng update sa built-in na application na ito? Hindi mawawala ang iyong umiiral na data. Hindi ito nangangailangan ng anumang espesyal na access."
+ "Hindi na-install ang app."
+ "Na-block ang pag-install sa package."
+ "Hindi na-install ang app dahil nagkakaproblema ang package sa isang dati nang package."
+ "Hindi na-install ang app dahil hindi tugma ang app sa iyong tablet."
+ "Hindi compatible ang app na ito sa iyong TV."
+ "Hindi na-install ang app dahil hindi tugma ang app sa iyong telepono."
+ "Hindi na-install ang app dahil lumalabas na di-wasto ang package."
+ "Hindi ma-install ang %1$s sa iyong tablet."
+ "Ang %1$s ay hindi ma-install sa iyong TV."
+ "Hindi ma-install ang %1$s sa iyong telepono."
+ "Buksan"
+ "Hindi pinapayagan ng iyong admin ang pag-install ng mga app na nakuha mula sa mga hindi kilalang pinagmulan"
+ "Hindi maaaring mag-install ang user na ito ng mga hindi kilalang app"
+ "Hindi pinapayagan ang user na mag-install ng mga app"
+ "OK"
+ "Pamahalaan ang apps"
+ "Wala ng espasyo"
+ "Hindi ma-install ang %1$s. Magbakante ng ilang espasyo at subukang muli."
+ "Hindi makita ang app"
+ "Hindi makita ang app sa listahan ng naka-install na apps."
+ "Hindi pinapayagan"
+ "Hindi pinapayagan ang kasalukuyang user na gawin ang pag-uninstall na ito."
+ "Error"
+ "Hindi ma-uninstall ang app."
+ "I-uninstall ang app"
+ "I-uninstall ang update"
+ "Bahagi ang %1$s ng sumusunod na app:"
+ "Gusto mo bang i-uninstall ang app na ito?"
+ "Nais mo bang i-uninstall ang app na ito para sa ""lahat"" ng user? Aalisin ang application at ang data nito mula sa ""lahat"" ng user sa device."
+ "Gusto mo bang i-uninstall ang app na ito para sa user na si %1$s?"
+ "Gusto mo bang palitan ang app na ito ng factory na bersyon? Maaalis ang lahat ng data."
+ "Gusto mo bang palitan ang app na ito ng factory na bersyon? Maaalis ang lahat ng data. Nakakaapekto ito sa lahat ng user ng device na ito, kasama ang mga may profile sa trabaho."
+ "Mga nasa proseso ng pag-uninstall"
+ "Mga hindi na-uninstall"
+ "Ina-uninstall…"
+ "Ina-uninstall ang %1$s…"
+ "Natapos ang pag-uninstall."
+ "Na-uninstall ang %1$s"
+ "Di-matagumpay ang pag-uninstall."
+ "Hindi na-uninstall ang %1$s."
+ "Hindi ma-uninstall ang aktibong app ng admin ng device"
+ "Hindi ma-uninstall ang aktibong app ng admin ng device para kay %1$s"
+ "Ang app na ito ay kailangan ng ilang user o profile at na-uninstall na ito sa iba pa"
+ "Ang app na ito ay kailangan para sa iyong profile at hindi maaaring i-uninstall."
+ "Kinakailangan app na ito ng administrator ng device mo at di maaari i-uninstall."
+ "Pamahalaan ang mga app ng admin ng device"
+ "Pamahalaan ang mga user"
+ "Hindi ma-install ang %1$s."
+ "Nagkaroon ng problema sa pag-parse sa package."
+ "Bago"
+ "Lahat"
+ "Privacy"
+ "Access sa Device"
+ "Walang kinakailangang mga bagong pagpapahintulot ang update na ito."
+ "Tanggihan"
+ "Higit pang impormasyon"
+ "Tanggihan pa rin"
+ "%1$s ng %2$s"
+ "Payagan ang <b>%1$s</b> na %2$s?"
+ "Palaging payagan ang <b>%1$s</b> na %2$s?"
+ "Habang ginagamit lang ang app"
+ "Palagi"
+ "Tanggihan at huwag nang itanong muli"
+ "%1$d ang naka-disable"
+ "naka-disable lahat"
+ "walang naka-disable"
+ "Payagan"
+ "Mga App"
+ "Mga pahintulot sa app"
+ "Huwag nang tatanunging muli"
+ "Walang pahintulot"
+ "Mga karagdagang pahintulot"
+ "Buksan ang impormasyon ng app"
+
+ %1$d pa
+ %1$d pa
+
+ "Ang app na ito ay idinisenyo para sa mas lumang bersyon ng Android. Kapag tinanggihan ang pahintulot, maaaring hindi na ito gumana ayon sa inaasahan."
+ "gumawa ng hindi kilalang pagkilos"
+ "Pinapayagan ang %1$d sa %2$d (na) app"
+ "Ipakita ang system"
+ "Itago ang system"
+ "Walang mga app"
+ "Mga Setting ng Lokasyon"
+ "Ang %1$s ay isang provider ng mga serbisyo sa lokasyon para sa device na ito. Mababago ang access sa lokasyon mula sa mga setting ng lokasyon."
+ "Kung tatanggihan mo ang pahintulot na ito, maaaring hindi na gumana ang mga pangunahing feature ng iyong device gaya ng inaasahan."
+ "Ipinapatupad sa pamamagitan ng patakaran"
+ "Na-disable ayon sa patakaran ang pag-access sa background"
+ "Na-enable ayon sa patakaran ang pag-access sa background"
+ "Na-enable ayon sa patakaran ang pag-access sa foreground"
+ "Kinokontrol ng admin"
+
+
+
+ "Palagi"
+ "Habang ginagamit lang ang app"
+ "Huwag Kailanman"
+ "Naglo-load..."
+ "Lahat ng pahintulot"
+ "Iba pang mga kakayahan ng app"
+ "Kahilingan sa pagpapahintulot"
+ "Natukoy ang overlay ng screen"
+ "Upang baguhin ang setting ng pahintulot na ito, kailangan mo munang i-off ang overlay ng screen mula sa Mga Setting > Mga App"
+ "Buksan ang mga setting"
+ "Android Wear"
+ "Ang mga pagkilos na I-install/I-uninstall ay hindi sinusuportahan sa Wear."
+ "Piliin kung ano ang papayagang i-access ng <b>%1$s</b>"
+ "Na-update na ang <b>%1$s</b>. Piliin kung ano ang papayagang i-access ng app na ito."
+ "Kanselahin"
+ "Magpatuloy"
+ "Mga bagong pahintulot"
+ "Mga kasalukuyang pahintulot"
+ "Inihahanda ang app…"
+ "Hindi Alam"
+ "Para sa iyong seguridad, hindi pinapayagan ang tablet mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."
+ "Para sa iyong seguridad, hindi pinapayagan ang TV mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."
+ "Para sa iyong seguridad, hindi pinapayagan ang telepono mo na mag-install ng mga hindi alam na app mula sa pinagmulang ito."
+ "Mas nanganganib ang iyong telepono at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa telepono mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."
+ "Mas nanganganib ang iyong tablet at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa tablet mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."
+ "Mas nanganganib ang iyong TV at personal na data sa mga pag-atake mula sa mga hindi kilalang app. Sa pamamagitan ng pag-install ng app na ito, sumasang-ayon ka na ikaw ang responsable sa anumang pinsala sa TV mo o pagkawala ng data na maaaring magresulta mula sa paggamit nito."
+ "Magpatuloy"
+ "Mga Setting"
+ "Ini-install/ina-uninstall ang wear apps"
+
diff --git a/packages/PackageInstaller/res/values-tr-television/strings.xml b/packages/PackageInstaller/res/values-tr-television/strings.xml
new file mode 100644
index 0000000000000..7ed0a6853a41b
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Reddet ve bir daha sorma"
+ "Bu ayarı daha sonra Ayarlar > Uygulamalar\'dan değiştirebilirsiniz"
+ "%1$s / %2$s"
+ "Sistem uygulamalarını göster"
+ "Uygulama izinleri"
+ "Uygulama izinleri"
+ "%1$s izinleri"
+ "Ek izinler"
+ "%1$s izinleri"
+
diff --git a/packages/PackageInstaller/res/values-tr-watch/strings.xml b/packages/PackageInstaller/res/values-tr-watch/strings.xml
new file mode 100644
index 0000000000000..fbc5b93085dae
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Reddet, bir daha sorma"
+ "%1$s / %2$s"
+ "Sistem uygulamalarını göster"
+ "Değiştirilemez"
+ "Evet"
+ "İptal"
+
diff --git a/packages/PackageInstaller/res/values-tr/strings.xml b/packages/PackageInstaller/res/values-tr/strings.xml
new file mode 100644
index 0000000000000..ef7882dfcfeab
--- /dev/null
+++ b/packages/PackageInstaller/res/values-tr/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paket yükleyici"
+ "Sonraki"
+ "Yükle"
+ "Bitti"
+ "İptal"
+ "Yükleniyor…"
+ "%1$s yükleniyor…"
+ "Uygulama yüklendi."
+ "Bu uygulamayı yüklemek istiyor musunuz? Uygulama şunlara erişebilecektir:"
+ "Bu uygulamayı yüklemek istiyor musunuz? Herhangi bir özel erişim gerektirmez."
+ "Bu mevcut uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz silinmeyecektir. Güncellenen uygulama şunlara erişebilecektir:"
+ "Bu yerleşik uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz silinmeyecektir. Güncellenen uygulama şunlara erişebilecektir:"
+ "Bu mevcut uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz kaybolacaktır. Herhangi bir özel erişim gerektirmez."
+ "Bu yerleşik uygulamaya ait bir güncellemeyi yüklemek istiyor musunuz? Mevcut verileriniz kaybolacaktır. Herhangi bir özel erişim gerektirmez."
+ "Uygulama yüklenmedi."
+ "Paketin yüklemesi engellendi."
+ "Paket, mevcut bir paketle çakıştığından uygulama yüklenemedi."
+ "Tabletinizle uyumlu olmadığından uygulama yüklenemedi."
+ "Bu uygulama TV\'niz ile uyumlu değil."
+ "Telefonunuzla uyumlu olmadığından uygulama yüklenemedi."
+ "Paket geçersiz göründüğünden uygulama yüklenemedi."
+ "%1$s bu tabletinize yüklenemedi."
+ "%1$s TV\'nize yüklenemedi."
+ "%1$s bu telefonunuza yüklenemedi."
+ "Aç"
+ "Yöneticiniz, bilinmeyen kaynaklardan edinilen uygulamaların yüklenmesine izin vermiyor"
+ "Bilinmeyen uygulamalar bu kullanıcı tarafından yüklenemez"
+ "Bu kullanıcının uygulama yüklemesine izin verilmiyor"
+ "Tamam"
+ "Uygulamaları yönet"
+ "Yer kalmadı"
+ "%1$s yüklenemedi. Boş alan açın ve yeniden deneyin."
+ "Uygulama bulunamadı"
+ "Uygulama, yüklü uygulamalar listesinde bulunamadı."
+ "İzin verilmiyor"
+ "Geçerli kullanıcının bu yüklemeyi kaldırma izni yok."
+ "Hata"
+ "Uygulamanın yüklemesi kaldırılamadı."
+ "Uygulamayı kaldır"
+ "Güncelleme kaldırılsın mı?"
+ "%1$s, şu uygulamanın bir parçasıdır:"
+ "Bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"
+ "Bu uygulamanın yüklemesini ""tüm"" kullanıcılar için kaldırmak istiyor musunuz? Uygulama ve verileri cihazdan ""tüm"" kullanıcılar için kaldırılacaktır."
+ "%1$s adlı kullanıcı için bu uygulamanın yüklemesini kaldırmak istiyor musunuz?"
+ "Bu uygulamayı fabrika sürümüyle değiştirmek istiyor musunuz? Tüm veriler silinecektir."
+ "Bu uygulamayı fabrika sürümüyle değiştirmek istiyor musunuz? Tüm veriler silinecektir. Bu, çalışma profilleri olan kullanıcılar da dahil olmak üzere cihazı kullanan tüm kullanıcıları etkiler."
+ "Devam eden yükleme kaldırma işlemleri"
+ "Başarısız yükleme kaldırma işlemleri"
+ "Kaldırılıyor…"
+ "%1$s uygulamasının yüklemesi kaldırılıyor…"
+ "Kaldırma işlemi tamamlandı."
+ "%1$s uygulamasının yüklemesi kaldırıldı"
+ "Yükleme kaldırılamadı."
+ "%1$s uygulamasının yüklemesi kaldırılamadı."
+ "Etkin cihaz yönetimi uygulamasının yüklemesi kaldırılamıyor"
+ "%1$s adlı kullanıcı için etkin cihaz yönetimi uygulamasının yüklemesi kaldırılamıyor"
+ "Bu uygulama bazı kullanıcılar veya profiller için gerekli ve diğerleri için uygulamanın yüklemesi kaldırıldı"
+ "Profiliniz için bu uygulama gerekli ve yüklemesi kaldırılamaz."
+ "Bu uygulama, cihazınızın yöneticisi için gereklidir ve yüklemesi kaldırılamaz."
+ "Cihaz yönetimi uygulamalarını yönet"
+ "Kullanıcıları yönetme"
+ "%1$s kaldırılamadı."
+ "Paketin ayrıştırılmasında bir sorun oluştu."
+ "Yeni"
+ "Tümü"
+ "Gizlilik"
+ "Cihaz Erişimi"
+ "Bu güncelleme yeni izin gerektirmiyor."
+ "Reddet"
+ "Daha fazla bilgi"
+ "Yine de reddet"
+ "%1$s / %2$s"
+ "<b>%1$s</b> uygulamasına %2$s izni verilsin mi?"
+ "<b>%1$s</b> uygulamasına %2$s için izin verilsin m?"
+ "Sadece uygulama kullanılırken"
+ "Her zaman"
+ "Reddet ve bir daha sorma"
+ "%1$d izin devre dışı"
+ "tümü devre dışı"
+ "hiçbiri devre dışı değil"
+ "İzin ver"
+ "Uygulamalar"
+ "Uygulama izinleri"
+ "Tekrar sorma"
+ "İzin yok"
+ "Ek izinler"
+ "Uygulama bilgilerini aç"
+
+ %1$d tane daha
+ %1$d tane daha
+
+ "Bu uygulama, Android\'in daha eski bir sürümü için tasarlandı. İznin reddedilmesi, uygulamanın bundan sonra amaçlandığı gibi çalışmamasına neden olabilir."
+ "bilinmeyen bir işlem gerçekleştirme"
+ "%1$d / %2$d uygulamaya izin veriliyor"
+ "Sistemi göster"
+ "Sistemi gizle"
+ "Uygulama yok"
+ "Konum Ayarları"
+ "%1$s, bu cihaz için konum hizmetlerinin bir sağlayıcısıdır. Konum erişimi, konum ayarlarından değiştirilebilir."
+ "Bu izni reddederseniz cihazınızın temel özellikleri artık beklendiği gibi çalışmayabilir."
+ "Politika tarafından zorunlu tutuldu"
+ "Arka plan erişimi politika tarafından devre dışı bırakıldı"
+ "Arka plan erişimi politika tarafından etkinleştirildi"
+ "Ön plan erişimi politika tarafından etkinleştirildi"
+ "Yönetici tarafından kontrol ediliyor"
+
+
+
+ "Her zaman"
+ "Sadece uygulama kullanılırken"
+ "Hiçbir zaman"
+ "Yükleniyor..."
+ "Tüm izinler"
+ "Diğer uygulama özellikleri"
+ "İzin isteği"
+ "Ekran yer paylaşımı tespit edildi"
+ "Bu izin ayarını değiştirmek için ilk olarak Ayarlar > Uygulamalar\'dan ekran yer paylaşımını kapatmanız gerekir"
+ "Ayarları aç"
+ "Android Wear"
+ "Yükleme/Yüklemeyi Kaldırma işlemleri Wear\'da desteklenmiyor."
+ "<b>%1$s</b> uygulamasının nelere erişmesine izin vereceğinizi seçin"
+ "<b>%1$s</b> güncellendi. Bu uygulamanın nelere erişmesine izin verileceğini seçin."
+ "İptal"
+ "Devam"
+ "Yeni izinler"
+ "Geçerli izinler"
+ "Uygulama hazırlanıyor…"
+ "Bilinmiyor"
+ "Güvenlik nedeniyle tabletinizin bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."
+ "Güvenlik nedeniyle TV\'nizin bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."
+ "Güvenlik nedeniyle telefonunuzun bu kaynaktan bilinmeyen uygulamalar yüklemesine izin verilmez."
+ "Telefonunuz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı telefonunuzda oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."
+ "Tabletiniz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı tabletinizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."
+ "TV\'niz ve kişisel verileriniz, bilinmeyen uygulamaların saldırılarına karşı daha savunmasızdır. Bu uygulamayı yükleyerek, uygulama kullanımından dolayı TV\'nizde oluşabilecek hasarın veya uğrayabileceğiniz veri kaybının sorumluluğunu kabul etmiş olursunuz."
+ "Devam"
+ "Ayarlar"
+ "Wear uyg. yükleme/yüklemesini kaldırma"
+
diff --git a/packages/PackageInstaller/res/values-uk-television/strings.xml b/packages/PackageInstaller/res/values-uk-television/strings.xml
new file mode 100644
index 0000000000000..f4b002f9a07a3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Відмовити й більше не запитувати"
+ "Можна змінити згодом у меню \"Налаштування\" > \"Додатки\""
+ "%1$s з %2$s"
+ "Показати системні додатки"
+ "Дозволи додатка"
+ "Дозволи додатка"
+ "%1$s: дозволи"
+ "Додаткові дозволи"
+ "%1$s: дозволи"
+
diff --git a/packages/PackageInstaller/res/values-uk-watch/strings.xml b/packages/PackageInstaller/res/values-uk-watch/strings.xml
new file mode 100644
index 0000000000000..e539d836c01bb
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Відхилити й більше не запитувати"
+ "%1$s з %2$s"
+ "Показати системні додатки"
+ "Не можна змінити"
+ "Так"
+ "Скасувати"
+
diff --git a/packages/PackageInstaller/res/values-uk/strings.xml b/packages/PackageInstaller/res/values-uk/strings.xml
new file mode 100644
index 0000000000000..bdc09dfd36b8a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uk/strings.xml
@@ -0,0 +1,158 @@
+
+
+
+
+ "Програма встановлення пакета"
+ "Далі"
+ "Установити"
+ "Готово"
+ "Скасувати"
+ "Встановлення…"
+ "Установлюється %1$s…"
+ "Додаток установлено."
+ "Установити цей додаток? Він отримає такі дозволи:"
+ "Установити цей додаток? Йому не потрібні спеціальні дозволи."
+ "Дійсно встановити оновлення для цієї наявної програми? Існуючі дані втрачено не буде. Оновлена програма отримає доступ до:"
+ "Дійсно встановити оновлення для цієї вбудованої програми? Існуючі дані втрачено не буде. Оновлена програма отримає доступ до:"
+ "Хочете встановити оновлення для наявної програми? Ваші наявні дані не зникнуть. Спеціальний доступ не потрібен."
+ "Хочете встановити оновлення для цієї вбудованої програми? Ваші наявні дані не зникнуть. Спеціальний доступ не потрібен."
+ "Додаток не встановлено."
+ "Встановлення пакета заблоковано."
+ "Додаток не встановлено, оскільки пакет конфліктує з наявним пакетом."
+ "Додаток не встановлено, оскільки він несумісний із вашим планшетом."
+ "Цей додаток несумісний із вашим телевізором."
+ "Додаток не встановлено, оскільки він несумісний із вашим телефоном."
+ "Додаток не встановлено, оскільки пакет недійсний."
+ "Програму %1$s неможливо встановити у вашому планшетному ПК."
+ "Не вдалося встановити додаток %1$s на ваш телевізор."
+ "Програму %1$s неможливо встановити у вашому телефоні."
+ "Відкрити"
+ "Ваш адміністратор заборонив установлювати додатки з невідомих джерел"
+ "Цей користувач не може встановлювати невідомі додатки"
+ "Цей користувач не може встановлювати додатки"
+ "OK"
+ "Керувати програмами"
+ "Недостат. місця"
+ "Програму %1$s неможливо встановити. Звільніть місце та повторіть спробу."
+ "Програму не знайдено"
+ "Програму не знайдено в списку встановлених програм."
+ "Заборонено"
+ "Поточний користувач не може видалити цей додаток."
+ "Помилка"
+ "Не вдалося видалити додаток."
+ "Видалити програму"
+ "Видалити оновлення"
+ "Дія %1$s є частиною такої програми:"
+ "Видалити додаток?"
+ "Хочете видалити цю програму для ""всіх"" користувачів? Програму та її дані буде видалено для ""всіх"" користувачів цього пристрою."
+ "Видалити цей додаток для користувача %1$s?"
+ "Відновити заводську версію цього додатка? Усі дані буде видалено."
+ "Відновити заводську версію цього додатка? Усі дані буде видалено. Це вплине на всіх користувачів цього пристрою, зокрема на користувачів із робочими профілями."
+ "Активні видалення"
+ "Невиконані видалення"
+ "Видалення..."
+ "Видалення додатка %1$s…"
+ "Видалення завершено."
+ "Додаток %1$s видалено"
+ "Видалення не здійснено."
+ "Не вдалося видалити додаток %1$s."
+ "Не вдається видалити активний додаток адміністратора пристрою"
+ "Не вдається видалити активний додаток адміністратора пристрою для користувача %1$s"
+ "Цей додаток потрібен для деяких користувачів чи профілів, але його було видалено для інших"
+ "Цей додаток потрібен для вашого профілю, тому його не можна видалити."
+ "Цей додаток не можна видалити – не дозволяє адміністратор пристрою."
+ "Керувати додатками адміністратора пристрою"
+ "Керувати користувачами"
+ "Програму %1$s неможливо видалити."
+ "Під час аналізу пакету виникла помилка."
+ "Нові"
+ "Усі"
+ "Конфіденційність"
+ "Доступ до пристрою"
+ "Для цього оновлення не потрібні нові дозволи."
+ "Відхилити"
+ "Докладніше"
+ "Усе одно заборонити"
+ "%1$s з %2$s"
+ "Дозволити додатку <b>%1$s</b> %2$s?"
+ "Завжди дозволяти додатку <b>%1$s</b> %2$s?"
+ "Лише коли додаток активний"
+ "Завжди"
+ "Відхилити й більше не запитувати"
+ "%1$d скасовано"
+ "усі скасовано"
+ "нічого не скасовано"
+ "Дозволити"
+ "Додатки"
+ "Дозволи додатків"
+ "Не запитувати знову"
+ "Немає дозволів"
+ "Додаткові дозволи"
+ "Відкрити інформацію про додаток"
+
+ Ще %1$d
+ Ще %1$d
+ Ще %1$d
+ Ще %1$d
+
+ "Цей додаток створено для старішої версії ОС Android. Якщо скасувати дозвіл, він може працювати неналежним чином."
+ "виконувати невідому дію"
+ "Додатки з дозволом: %1$d з %2$d"
+ "Показати системні додатки"
+ "Сховати системні додатки"
+ "Немає додатків"
+ "Налаштування геоданих"
+ "%1$s є постачальником служб локації для цього пристрою. Доступом до місцезнаходження можна керувати в налаштуваннях геоданих."
+ "Якщо скасувати цей дозвіл, основні функції вашого пристрою можуть працювати неналежним чином."
+ "Застосовується правилом"
+ "Доступ у фоновому режимі вимкнено правилом"
+ "Доступ у фоновому режимі ввімкнено правилом"
+ "Доступ в активному режимі ввімкнено правилом"
+ "Керує адміністратор"
+
+
+
+ "Завжди"
+ "Лише коли додаток активний"
+ "Ніколи"
+ "Завантаження…"
+ "Усі дозволи"
+ "Інші дозволи додатка"
+ "Запит на дозвіл"
+ "Виявлено накладання на екрані"
+ "Щоб змінити налаштування цього дозволу, спершу вимкніть накладання на екрані в меню \"Налаштування\" > \"Додатки\""
+ "Відкрити налаштування"
+ "Android Wear"
+ "Дії \"установити\" або \"видалити\" не підтримуються на пристроях Android Wear."
+ "Виберіть, до чого <b>%1$s</b> матиме доступ"
+ "<b>%1$s</b> оновлено. Виберіть, до чого цей додаток матиме доступ."
+ "Скасувати"
+ "Продовжити"
+ "Нові дозволи"
+ "Поточні дозволи"
+ "Підготовка додатка…"
+ "Невідомо"
+ "З міркувань безпеки на вашому планшеті заборонено встановлювати невідомі додатки з цього джерела."
+ "З міркувань безпеки на вашому телевізорі заборонено встановлювати невідомі додатки з цього джерела."
+ "З міркувань безпеки на вашому телефоні заборонено встановлювати невідомі додатки з цього джерела."
+ "Ваш телефон і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження телефона чи втрату даних унаслідок використання додатка."
+ "Ваш планшет і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження планшета чи втрату даних унаслідок використання додатка."
+ "Ваш телевізор і особисті дані більш уразливі до атак невідомих додатків. Установлюючи цей додаток, ви берете на себе відповідальність за пошкодження телевізора чи втрату даних унаслідок використання додатка."
+ "Продовжити"
+ "Налаштування"
+ "Встановлення або видалення додатків Wear"
+
diff --git a/packages/PackageInstaller/res/values-ur-television/strings.xml b/packages/PackageInstaller/res/values-ur-television/strings.xml
new file mode 100644
index 0000000000000..27089dd053e62
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "انکار کریں اور دوبارہ مت پوچھیں"
+ "آپ بعد میں ترتیبات > ایپس میں جا کر اسے تبدیل کرسکتے ہیں"
+ "%1$s / %2$s"
+ "سسٹم ایپس دکھائیں"
+ "ایپ کی اجازتیں"
+ "ایپ کی اجازتیں"
+ "%1$s اجازتیں"
+ "اضافی اجازتیں"
+ "%1$s اجازتیں"
+
diff --git a/packages/PackageInstaller/res/values-ur-watch/strings.xml b/packages/PackageInstaller/res/values-ur-watch/strings.xml
new file mode 100644
index 0000000000000..197ac8497837e
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "انکار کریں، دوبارہ مت پوچھیں"
+ "%1$s / %2$s"
+ "سسٹم ایپس دکھائیں"
+ "ناقابل تبدیل"
+ "ہاں"
+ "منسوخ کریں"
+
diff --git a/packages/PackageInstaller/res/values-ur/strings.xml b/packages/PackageInstaller/res/values-ur/strings.xml
new file mode 100644
index 0000000000000..78135a604ebd7
--- /dev/null
+++ b/packages/PackageInstaller/res/values-ur/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "پیکیج انسٹال کنندہ"
+ "اگلا"
+ "انسٹال کریں"
+ "ہو گیا"
+ "منسوخ کریں"
+ "انسٹال کیا جا رہا ہے…"
+ "%1$s کو انسٹال کیا جا رہا ہے…"
+ "ایپ انسٹال ہوگئی۔"
+ "کیا آپ یہ ایپلیکیشن انسٹال کرنا چاہتے ہیں؟ اس کو مندرجہ ذیل تک رسائی حاصل ہوگی:"
+ "کیا آپ یہ ایپلیکیشن انسٹال کرنا چاہتے ہیں؟ اس کو کوئی خاص رسائی درکار نہیں۔"
+ "کیا آپ اس موجودہ ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اپ ڈیٹ کردہ ایپلیکیشن کو مندرجہ ذیل تک رسائی حاصل ہوگی:"
+ "کیا آپ پہلے سے شامل اس ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اپ ڈیٹ کردہ ایپلیکیشن کو مندرجہ ذیل تک رسائی حاصل ہوگی:"
+ "کیا آپ اس موجودہ ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اس کو کوئی خاص رسائی درکار نہیں۔"
+ "کیا آپ پہلے سے شامل اس ایپلیکیشن میں ایک اپ ڈیٹ انسٹال کرنا چاہتے ہیں؟ آپ کا موجودہ ڈیٹا ضائع نہیں ہوگا۔ اس کو کوئی خاص رسائی درکار نہیں۔"
+ "ایپ انسٹال نہیں ہوئی۔"
+ "پیکج کو انسٹال ہونے سے روک دیا گیا۔"
+ "ایپ کو پیکج کے بطور انسٹال نہیں کیا گیا کیونکہ پیکج ایک موجودہ پیکیج سے متصادم ہے۔"
+ "ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے ٹیبلیٹ کے ساتھ مطابقت پذیر نہیں ہے۔"
+ "یہ ایپ آپ کے TV کے ساتھ مطابقت پذیر نہیں ہے۔"
+ "ایپ انسٹال نہیں ہوئی کیونکہ ایپ آپ کے فون کے ساتھ مطابقت پذیر نہیں ہے۔"
+ "ایپ انسٹال نہیں ہوئی کیونکہ پیکیج غلط معلوم ہوتا ہے۔"
+ "%1$s کو آپ کے ٹیبلیٹ پر انسٹال نہیں کیا جا سکا۔"
+ "%1$s کو آپ کے TV پر انسٹال نہیں کیا جا سکا۔"
+ "%1$s کو آپ کے فون پر انسٹال نہیں کیا جا سکا۔"
+ "کھولیں"
+ "آپ کا منتظم نامعلوم ذرائع سے اخذ کردہ ایپس کو انسٹال کرنے کی اجازت نہیں دیتا ہے"
+ "اس صارف کے ذریعے نامعلوم ایپس کو انسٹال نہیں کیا جا سکتا"
+ "اس صارف کو ایپس انسٹال کرنے کی اجازت نہیں ہے"
+ "ٹھیک ہے"
+ "ایپس کا نظم کریں"
+ "جگہ ختم ہو گئی ہے"
+ "%1$s کو انسٹال نہیں کیا جا سکا۔ کچھ جگہ خالی کریں اور دوبارہ کوشش کریں۔"
+ "ایپ نہیں ملی"
+ "ایپ انسٹال کردہ ایپس کی فہرست میں نہیں ملی۔"
+ "اجازت نہیں ہے"
+ "موجودہ صارف کو اس ان انسٹالیشن کو سرانجام دینے کی اجازت نہیں ہے۔"
+ "خرابی"
+ "ایپ ان انسٹال نہیں ہو سکی۔"
+ "ایپ کو اَن انسٹال کریں"
+ "اپ ڈیٹ اَن انسٹال کریں"
+ "%1$s درج ذیل ایپ کا حصہ ہے:"
+ "کیا آپ یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟"
+ "کیا آپ ""سبھی"" صارفین کیلئے یہ ایپ اَن انسٹال کرنا چاہتے ہیں؟ ایپلیکیشن اور اس کا ڈیٹا آلے پر موجود ""سبھی"" صارفین سے ہٹا دیا جائے گا۔"
+ "کیا آپ اس ایپ کو صارف %1$s کیلئے اَن انسٹال کرنا چاہتے ہیں؟"
+ "اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔"
+ "اس ایپ کو فیکٹری ورژن سے تبدیل کریں؟ تمام ڈیٹا ہٹا دیا جائے گا۔ اس سے اس آلہ کے تمام صارف متاثر ہوں گے بشمول ان کے جن کے پاس دفتری پروفائلز ہیں۔"
+ "چل رہے اَن انسٹالس"
+ "ناکام اَن انسٹالس"
+ "اَن انسٹال ہو رہا ہے…"
+ "%1$s ان انسٹال ہو رہی ہے…"
+ "اَن انسٹال پورا ہوگیا۔"
+ "%1$s ان انسٹال ہو گیا"
+ "اَن انسٹال ناکام۔"
+ "%1$s کو ان انسٹال کرنا کامیاب نہیں ہوا۔"
+ "فعال آلہ کے منتظم کی ایپ اَن انسٹال نہیں کر سکتے"
+ "فعال آلہ کے منتظم کی ایپ %1$s کیلئے اَن انسٹال نہیں کر سکتے"
+ "یہ ایپ کچھ صارفین اور پروفائلوں کیلئے درکار ہے اور دیگر کیلئے ان انسٹال ہو گئی"
+ "یہ ایپ آپ کے پروفائل کیلئے درکار ہے اور یہ ان انسٹال نہیں ہو سکتی۔"
+ "یہ ایپ آپ کے آلہ کے منتظم کو درکار ہے اور اسے اَن انسٹال نہیں کیا جا سکتا ہے۔"
+ "آلہ کے منتظم کی ایپس کا نظم کریں"
+ "صارفین کا نظم کریں"
+ "%1$s کو اَن انسٹال نہیں کیا جا سکا۔"
+ "پیکیج کو پارس کرنے میں ایک دشواری پیش آگئی۔"
+ "نئی"
+ "سبھی"
+ "رازداری"
+ "آلہ کی رسائی"
+ "اس اپ ڈیٹ کو کوئی نئی اجازتیں درکار نہیں۔"
+ "مسترد کریں"
+ "مزید معلومات"
+ "بہرصورت انکار کریں"
+ "%1$s از %2$s"
+ "<b>%1$s</b> کو %2$s کی اجازت دیں؟"
+ "ہمیشہ <b>%1$s</b> کو %2$s کی اجازت دیں؟"
+ "صرف ایپ استعمال کرنے کے دوران"
+ "ہمیشہ"
+ "انکار کریں اور دوبارہ مت پوچھیں"
+ "%1$d غیر فعال ہو گئیں"
+ "تمام غیر فعال ہو گئیں"
+ "کچھ بھی غیر فعال نہیں ہوا"
+ "اجازت دیں"
+ "ایپس"
+ "ایپ کی اجازتیں"
+ "دوبارہ مت پوچھیں"
+ "کوئی اجازتیں نہیں ہیں"
+ "اضافی اجازتیں"
+ "ایپ کی معلومات کھولیں"
+
+ %1$d مزید
+ %1$d مزید
+
+ "یہ ایپ Android کے ایک پرانے ورژن کیلئے ڈیزائن کی گئی تھی۔ اجازت دینے سے انکار کرنے پر ممکن ہے کہ وہ مزید ٹھیک سے کام نہ کرے۔"
+ "ایک نامعلوم کارروائی کو انجام دیں"
+ "%2$d میں سے %1$d ایپس کو اجازت دے دی گئی"
+ "سسٹم دکھائیں"
+ "سسٹم چھپائیں"
+ "کوئی ایپس نہیں ہیں"
+ "مقام کی ترتیبات"
+ "%1$s اس آلہ کیلئے مقام کی سروسز کا فراہم کنندہ ہے۔ مقام کی رسائی میں مقام کی ترتیبات سے ترمیم کی جا سکتی ہے۔"
+ "اگرآپ اس اجازت کو مسترد کرتے ہیں تو شاید آپ کے آلہ کی بنیادی خصوصیات ٹھیک سے کام نہ کریں۔"
+ "پالیسی کی طرف سے نافذ کردہ"
+ "پالیسی نے پس منظر کی رسائی غیر فعال کر دی ہے"
+ "پالیسی نے پس منظر کی رسائی فعال کر دی ہے"
+ "پالیسی نے پیش منظر کی رسائی فعال کر دی ہے"
+ "کنٹرول کردہ بذریعہ منتظم"
+
+
+
+ "ہمیشہ"
+ "صرف ایپ استعمال کرنے کے دوران"
+ "کبھی نہیں"
+ "لوڈ ہورہا ہے…"
+ "تمام اجازاتیں"
+ "دوسری ایپ اہلیتیں"
+ "اجازت کی درخواست"
+ "اسکرین اورلے کا پتہ چلا ہے"
+ "اس اجازت کی ترتیب کو تبدیل کرنے کیلئے آپ کو پہلے ترتیبات > Apps سے سکرین اورلے آف کرنا ہوگا"
+ "ترتیبات کھولیں"
+ "Android Wear"
+ "\'کاروائیاں انسٹال/ان انسٹال کریں\' Wear پر تعاون یافتہ نہیں ہے۔"
+ "انتخاب کریں کہ <b>%1$s</b> کو کس تک رسائی کی اجازت دینی ہے"
+ "<b>%1$s</b> اپ ڈیٹ ہو گئی ہے۔ انتخاب کریں کہ اس ایپ کو کس تک رسائی کی اجازت دینی ہے۔"
+ "منسوخ کریں"
+ "جاری رکھیں"
+ "نئی اجازتیں"
+ "موجودہ اجازتیں"
+ "ایپ کی مرحلہ بندی ہو رہی ہے…"
+ "نامعلوم"
+ "آپ کی سیکیوریٹی کیلئے، آپ کے ٹیبلیٹ کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"
+ "آپ کی سیکیوریٹی کیلئے، آپ کے TV کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"
+ "آپ کی سیکیوریٹی کیلئے، آپ کے فون کو اس ذریعے سے نامعلوم ایپس انسٹال کرنے کی اجازت نہیں ہے۔"
+ "آپ کے فون اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے فون کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"
+ "آپ کے ٹیبلیٹ اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے ٹیبلیٹ کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"
+ "آپ کے TV اور ذاتی ڈیٹا کو نامعلوم ایپس کی جانب سے حملے کا زیادہ خطرہ ہے۔ اس ایپ کو انسٹال کر کے، آپ اس بات سے اتفاق کرتے ہیں کہ آپ اس سے اپنے TV کو ہونے والے کسی بھی نقصان یا ڈیٹا کے نقصان کیلئے خود ذمہ دار ہیں۔"
+ "جاری رکھیں"
+ "ترتیبات"
+ "wear ایپس کا انسٹال/ان انسٹال کرنا"
+
diff --git a/packages/PackageInstaller/res/values-uz-television/strings.xml b/packages/PackageInstaller/res/values-uz-television/strings.xml
new file mode 100644
index 0000000000000..bcc12ac073c22
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Rad etilsin va boshqa so‘ralmasin"
+ "Siz buni keyinroq Sozlamalar > Ilovalar bo‘limi orqali ham o‘zgartirishingiz mumkin"
+ "%1$s / %2$s"
+ "Tizim ilovalarini ko‘rsatish"
+ "Ilova uchun ruxsatlar"
+ "Ilova uchun ruxsatlar"
+ "%1$s uchun ruxsat"
+ "Qo‘shimcha ruxsatlar"
+ "%1$s uchun ruxsat"
+
diff --git a/packages/PackageInstaller/res/values-uz-watch/strings.xml b/packages/PackageInstaller/res/values-uz-watch/strings.xml
new file mode 100644
index 0000000000000..83f8da7f70397
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Rad etilsin va boshqa so‘ralmasin"
+ "%1$s / %2$s"
+ "Tizim ilovalarini ko‘rsatish"
+ "O‘zgartirilmaydi"
+ "Ha"
+ "Bekor qilish"
+
diff --git a/packages/PackageInstaller/res/values-uz/strings.xml b/packages/PackageInstaller/res/values-uz/strings.xml
new file mode 100644
index 0000000000000..15a2e92ee3d5c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-uz/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Paket o‘rnatish vositasi"
+ "Keyingisi"
+ "O‘rnatish"
+ "Tayyor"
+ "Bekor qilish"
+ "O‘rnatilmoqda…"
+ "%1$s o‘rnatilmoqda…"
+ "Ilova o‘rnatildi."
+ "Bu ilovani o‘rnatmoqchimisiz? U quyidagi ruxsatlarga ega:"
+ "Bu ilovani o‘rnatmoqchimisiz? U hech qanday maxsus ruxsat talab qilmaydi."
+ "Bu ilova uchun yangilanishni o‘rnatmoqchimisiz? Yangilanganidan keyin u quyidagi ruxsatlarga ega bo‘ladi:"
+ "Bu ilova uchun yangilanishni o‘rnatmoqchimisiz? Yangilanganidan keyin u quyidagi ruxsatlarga ega bo‘ladi:"
+ "Ushbu mavjud ilovaga yangilanish o‘rnatilsinmi? Mavjud ma’lumotlaringiz o‘chib ketmaydi. U hech qanday maxsus ruxsat talab qilmaydi."
+ "Ushbu tizim ilovasiga yangilanish o‘rnatilsinmi? Mavjud ma’lumotlaringiz o‘chib ketmaydi. U hech qanday maxsus ruxsat talab qilmaydi."
+ "Ilova o‘rnatilmadi."
+ "Paket o‘rnatilishga qarshi bloklangan."
+ "Paket mavjud paket bilan zid kelganligi uchun ilovani o‘rnatib bo‘lmadi."
+ "Ilova planshetingizga mos kelmaganligi uchun uni o‘rnatib bo‘lmadi."
+ "Bu ilova televizoringiz bilan mos emas."
+ "Ilova telefoningizga mos kelmaganligi uchun uni o‘rnatib bo‘lmadi."
+ "Paket yaroqsiz bo‘lganligi uchun ilovani o‘rnatib bo‘lmadi."
+ "%1$s planshetingizga o‘rnatilmadi."
+ "%1$s ilovasini televizoringizga o‘rnatib bo‘lmadi."
+ "%1$s telefoningizga o‘rnatilmadi."
+ "Ochish"
+ "Administratoringiz begona manbalardan olingan ilovalarni o‘rnatishga ruxsat bermagan"
+ "Notanish ilovalarni bu foydalanuvchi tomonidan o‘rnatib bo‘lmaydi"
+ "Bu foydalanuvchiga ilovalarni o‘rnatish uchun ruxsat berilmagan"
+ "OK"
+ "Ilovalarni boshqarish"
+ "Joy qolmadi"
+ "%1$s o‘rnatilmadi. Xotiradan biroz joy bo‘shating va qaytadan urinib ko‘ring."
+ "Ilova topilmadi"
+ "Ilova o‘rnatilgan ilovalar ro‘yxatidan topilmadi."
+ "Ruxsat berilmagan"
+ "Joriy foydalanuvchiga bu o‘chirishni amalga oshirishi uchun ruxsat berilmagan."
+ "Xato"
+ "Ilovani o‘chirib bo‘lmadi"
+ "Ilovani o‘chirish"
+ "Yangilanishni o‘chirish"
+ "%1$s quyidagi ilovaning bir qismidir:"
+ "Bu ilova o‘chirib tashlansinmi?"
+ "Ushbu ilova ""barcha"" foydalanuvchilar uchun o‘chirilsinmi? Ilova va uning ma’lumotlari qurilmadagi ""barcha"" foydalanuvchilardan o‘chib ketadi."
+ "Haqiqatdan ham %1$s foydalanuvchi uchun ushbu ilovani olib tashlamoqchimisiz?"
+ "Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha ma’lumotlar o‘chirib tashlanadi."
+ "Bu ilova boshlang‘ich versiyasi bilan almashtirilsinmi? Barcha ma’lumotlar o‘chirib tashlanadi. Bu qurilmaning barcha foydalanuvchilariga, jumladan, ularning ishchi profillariga ham ta’sir qiladi."
+ "Davom etayotganlar"
+ "Amalga oshmaganlar"
+ "O‘chirilmoqda…"
+ "%1$s o‘chirilmoqda…"
+ "O‘chirib tashlandi."
+ "%1$s o‘chirib tashlandi"
+ "O‘chirish muvaffaqiyatsizlikka uchradi."
+ "%1$s ilovasini o‘chirib bo‘lmadi."
+ "Faol qurilma administratori ilovasini o‘chirib bo‘lmaydi"
+ "%1$s profilida faol qurilma administratori ilovasini o‘chirib bo‘lmaydi"
+ "Bu ilova ba’zi foydalanuvchi yoki profillar uchun zarur, boshqalar uchun esa o‘chirib tashlangan"
+ "Bu ilova profilingiz uchun kerak va uni o‘chirib bo‘lmaydi."
+ "Ushbu ilova qurilmangiz ma\'muri tomonidan ishlatiladi, shuning uchun uni olib tashlab bo\'lmaydi."
+ "Qurilma administratori ilovalarini boshqarish"
+ "Foydalanuvchilarni boshqarish"
+ "%1$s o‘chirilmadi."
+ "Paketni tahlil qilishda muammo yuz berdi."
+ "Yangi"
+ "Barchasi"
+ "Maxfiylik"
+ "Qurilmalardan foydalanish"
+ "Ushbu yangilanish hech qanday yangi ruxsatlarni talab qilmaydi."
+ "Rad etish"
+ "Batafsil"
+ "Baribir rad etilsin"
+ "%1$s / %2$s"
+ "<b>%1$s</b> ilovasiga %2$s uchun ruxsat berilsinmi?"
+ "<b>%1$s</b> ilovasiga bu amalga bajarishga doim ruxsat berilsinmi: %2$s?"
+ "Faqat ilova ishlatilayotganda"
+ "Har doim"
+ "Rad etilsin va boshqa so‘ralmasin"
+ "%1$d tasi o‘chiq"
+ "hammasi o‘chiq"
+ "hech qaysi o‘chirilmagan"
+ "Ruxsat berish"
+ "Ilovalar"
+ "Ilovalar uchun ruxsatlar"
+ "Boshqa so‘ralmasin"
+ "Hech narsa topilmadi"
+ "Qo‘shimcha ruxsatlar"
+ "Ilovaga oid ma’lumotni ochish"
+
+ Yana %1$d ta
+ Yana %1$d ta
+
+ "Bu ilova Androidning eskiroq versiyasiga mo‘ljallab ishlab chiqilgan. Agar ruxsat bermasangiz, u kutilganidek ishlamasligi mumkin."
+ "noma’lum amalni bajarish"
+ "Ruxsat berilgan: %1$d/%2$d"
+ "Tizimga oid jarayonlar"
+ "Tizimga oid jarayonlarni berkitish"
+ "Hech qanday ilova yo‘q"
+ "Joylashuv sozlamalari"
+ "%1$s bu qurilma uchun joylashuvni aniqlash xizmatini taqdim etuvchi ilova hisoblanadi. Joylashuv ma’lumotlariga kirish vakolatini joylashuv sozlamalaridan o‘zgartirish mumkin."
+ "Agar bu ruxsatni rad qilsangiz, qurilmangizning asosiy funksiyalari bundan buyon kutilganidek ishlamasligi mumkin."
+ "Qoidaga muvofiq"
+ "Fon rejimida kirish qoidaga muvofiq taqiqlangan"
+ "Fon rejimida kirish qoidaga muvofiq yoqilgan"
+ "Faol rejimda kirish qoidaga muvofiq yoqilgan"
+ "Administrator tomonidan boshqariladi"
+
+
+
+ "Har doim"
+ "Faqat ilova ishlatilayotganda"
+ "Hech qachon"
+ "Yuklanmoqda…"
+ "Barcha ruxsatnomalar"
+ "Ilovaning boshqa imkoniyatlari"
+ "Ruxsatnoma so‘rovi"
+ "Boshqa oynalar ustidan ochiladigan ilova aniqlandi"
+ "Bu ruxsatnoma parametrini o‘zgartirish uchun avval Sozlamalar > Ilovalar bo‘limidan ekran ustidan ochilish funksiyasini o‘chirib qo‘ying"
+ "Sozlamalarni ochish"
+ "Android Wear"
+ "Wear qurilmasi o‘rnatish/o‘chirish amallarini qo‘llab-quvvatlamaydi."
+ "<b>%1$s</b> uchun beriladigan ruxsatlarni tanlang"
+ "<b>%1$s</b> yangilandi. Unga beriladigan ruxsatlarni tanlang."
+ "Bekor qilish"
+ "Davom etish"
+ "Yangi ruxsatnomalar"
+ "Joriy ruxsatnomalar"
+ "Kutib turing…"
+ "Noma’lum"
+ "Xavfsizlik yuzasidan, planshetingizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."
+ "Xavfsizlik yuzasidan, televizoringizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."
+ "Xavfsizlik yuzasidan, telefoningizga bu manbadan notanish ilovalarni o‘rnatishga ruxsat berilmagan."
+ "Telefoningiz va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan telefoningizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."
+ "Planshetingiz va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan planshetingizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."
+ "TV va shaxsiy ma‘lumotlaringiz notanish ilovalar xujumiga zaif bo‘ladi. Bu ilovani o‘rnatish bilan televizoringizga yetkaziladigan shikast va ma‘lumotlaringizni o‘chirib yuborilishiga javobgarlikni o‘z zimmangizga olasiz."
+ "Davom etish"
+ "Sozlamalar"
+ "Wear ilovalarini o‘rnatish/o‘chirish"
+
diff --git a/packages/PackageInstaller/res/values-vi-television/strings.xml b/packages/PackageInstaller/res/values-vi-television/strings.xml
new file mode 100644
index 0000000000000..574bde7f1431a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Từ chối và không hỏi lại"
+ "Bạn có thể thay đổi cài đặt này sau trong Cài đặt > Ứng dụng"
+ "%1$s / %2$s"
+ "Hiển thị ứng dụng hệ thống"
+ "Quyền của ứng dụng"
+ "Quyền của ứng dụng"
+ "Quyền %1$s"
+ "Quyền bổ sung"
+ "Quyền %1$s"
+
diff --git a/packages/PackageInstaller/res/values-vi-watch/strings.xml b/packages/PackageInstaller/res/values-vi-watch/strings.xml
new file mode 100644
index 0000000000000..d63ef28646548
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Từ chối, không hỏi lại"
+ "%1$s / %2$s"
+ "Hiển thị ứng dụng hệ thống"
+ "Ko thể thay đổi"
+ "Có"
+ "Hủy"
+
diff --git a/packages/PackageInstaller/res/values-vi/strings.xml b/packages/PackageInstaller/res/values-vi/strings.xml
new file mode 100644
index 0000000000000..09998d8d6aa01
--- /dev/null
+++ b/packages/PackageInstaller/res/values-vi/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Trình cài đặt gói"
+ "Tiếp theo"
+ "Cài đặt"
+ "Xong"
+ "Hủy"
+ "Đang cài đặt…"
+ "Đang cài đặt %1$s…"
+ "Ứng dụng đã được cài đặt."
+ "Bạn có muốn cài đặt ứng dụng này không? Ứng dụng sẽ có quyền truy cập vào:"
+ "Bạn có muốn cài đặt ứng dụng này không? Ứng dụng này không yêu cầu bất kỳ quyền truy cập đặc biệt nào."
+ "Bạn có muốn cài đặt bản cập nhật cho ứng dụng hiện tại này không? Dữ liệu hiện tại của bạn sẽ không bị mất. Ứng dụng đã cập nhật sẽ có quyền truy cập vào:"
+ "Bạn có muốn cài đặt bản cập nhật cho ứng dụng được cài sẵn này không? Dữ liệu hiện tại của bạn sẽ không bị mất. Ứng dụng được cập nhật sẽ có quyền truy cập vào:"
+ "Bạn có muốn cài đặt bản cập nhật cho ứng dụng hiện có này không? Dữ liệu hiện có của bạn sẽ không bị mất. Việc cài đặt không yêu cầu bất kỳ quyền truy cập đặc biệt nào."
+ "Bạn có muốn cài đặt bản cập nhật cho ứng dụng cài sẵn này không? Dữ liệu hiện có của bạn sẽ không bị mất. Việc cài đặt không yêu cầu quyền truy cập đặc biệt nào."
+ "Ứng dụng chưa được cài đặt."
+ "Đã chặn cài đặt gói."
+ "Ứng dụng chưa được cài đặt dưới dạng gói xung đột với gói hiện có."
+ "Ứng dụng chưa được cài đặt dưới dạng ứng dụng không tương thích với máy tính bảng của bạn."
+ "Ứng dụng này không tương thích với TV của bạn."
+ "Ứng dụng chưa được cài đặt dưới dạng ứng dụng không tương thích với điện thoại của bạn."
+ "Ứng dụng chưa được cài đặt dưới dạng gói dường như không hợp lệ."
+ "Không thể cài đặt %1$s trên máy tính bảng của bạn."
+ "Không cài đặt được %1$s trên TV của bạn."
+ "Không thể cài đặt %1$s trên điện thoại này."
+ "Mở"
+ "Quản trị viên của bạn không cho phép cài đặt ứng dụng từ nguồn không xác định"
+ "Người dùng này không thể cài đặt ứng dụng không xác định"
+ "Người dùng này không được phép cài đặt ứng dụng"
+ "OK"
+ "Quản lý ứng dụng"
+ "Hết dung lượng"
+ "Không thể cài đặt %1$s. Hãy giải phóng dung lượng và thử lại."
+ "Không tìm thấy ứng dụng"
+ "Không tìm thấy ứng dụng trong danh sách các ứng dụng đã cài đặt."
+ "Không được phép"
+ "Người dùng hiện tại không được phép thực hiện quá trình gỡ cài đặt này."
+ "Lỗi"
+ "Không thể gỡ cài đặt ứng dụng."
+ "Gỡ cài đặt ứng dụng"
+ "Gỡ cài đặt cập nhật"
+ "%1$s là một phần của ứng dụng sau:"
+ "Bạn có muốn gỡ cài đặt ứng dụng này không?"
+ "Bạn có muốn gỡ cài đặt ứng dụng này cho ""tất cả"" người dùng không? Ứng dụng và dữ liệu của ứng dụng sẽ bị xóa khỏi ""tất cả"" người dùng trên thiết bị."
+ "Bạn có muốn gỡ cài đặt ứng dụng này cho người dùng %1$s?"
+ "Thay thế ứng dụng này bằng phiên bản gốc? Tất cả dữ liệu sẽ bị xóa."
+ "Thay thế ứng dụng này bằng phiên bản gốc? Tất cả dữ liệu sẽ bị xóa. Điều này ảnh hưởng đến tất cả người dùng thiết bị này, bao gồm cả những người có hồ sơ công việc."
+ "Gỡ cài đặt đang chạy"
+ "Gỡ cài đặt không thành công"
+ "Đang gỡ cài đặt..."
+ "Đang gỡ cài đặt %1$s…"
+ "Gỡ cài đặt đã hoàn tất."
+ "Đã gỡ cài đặt %1$s"
+ "Gỡ cài đặt không thành công."
+ "Gỡ cài đặt %1$s không thành công."
+ "Không thể gỡ cài đặt ứng dụng dành cho quản trị viên thiết bị đang hoạt động"
+ "Không thể gỡ cài đặt ứng dụng dành cho quản trị viên thiết bị đang hoạt động cho %1$s"
+ "Ứng dụng này bắt buộc với một số người dùng hoặc hồ sơ và được gỡ cài đặt cho người khác"
+ "Ứng dụng này là cần thiết cho hồ sơ của bạn và không thể gỡ cài đặt."
+ "Ứng dụng này được quản trị viên thiết bị của bạn yêu cầu và không thể gỡ cài đặt."
+ "Quản lý ứng dụng quản trị thiết bị"
+ "Quản lý người dùng"
+ "Không thể gỡ cài đặt %1$s"
+ "Đã xảy ra sự cố khi phân tích cú pháp gói."
+ "Mới"
+ "Tất cả"
+ "Bảo mật"
+ "Truy cập thiết bị"
+ "Bản cập nhật này không yêu cầu quyền mới."
+ "Từ chối"
+ "Thông tin khác"
+ "Vẫn từ chối"
+ "%1$s / %2$s"
+ "Cho phép <b>%1$s</b> %2$s?"
+ "Luôn cho phép <b>%1$s</b> %2$s?"
+ "Chỉ khi sử dụng ứng dụng"
+ "Luôn luôn"
+ "Từ chối và không hỏi lại"
+ "Đã vô hiệu hóa %1$d"
+ "tất cả quyền đều bị vô hiệu hóa"
+ "không có quyền nào bị vô hiệu hóa"
+ "Cho phép"
+ "Ứng dụng"
+ "Quyền của ứng dụng"
+ "Không hỏi lại"
+ "Không có quyền"
+ "Quyền khác"
+ "Mở thông tin về ứng dụng"
+
+ %1$d quyền khác
+ %1$d quyền khác
+
+ "Ứng dụng này được thiết kế cho các phiên bản Android cũ hơn. Từ chối quyền có thể làm cho ứng dụng không còn hoạt động như mong muốn."
+ "thực hiện hành động không xác định"
+ "Đã cho phép %1$d trong số %2$d ứng dụng"
+ "Hiển thị hệ thống"
+ "Ẩn hệ thống"
+ "Không có ứng dụng"
+ "Cài đặt vị trí"
+ "%1$s là nhà cung cấp dịch vụ vị trí cho thiết bị này. Bạn có thể sửa đổi quyền truy cập vị trí từ cài đặt vị trí."
+ "Nếu bạn từ chối quyền này, các tính năng cơ bản trên thiết bị của bạn có thể không còn hoạt động như dự kiến."
+ "Được thực thi bằng chính sách"
+ "Quyền truy cập nền bị tắt theo chính sách"
+ "Quyền truy cập nền được bật theo chính sách"
+ "Quyền truy cập nền trước được bật theo chính sách"
+ "Do quản trị viên kiểm soát"
+
+
+
+ "Luôn luôn"
+ "Chỉ khi sử dụng ứng dụng"
+ "Không bao giờ"
+ "Đang tải…"
+ "Tất cả các quyền"
+ "Các khả năng khác của ứng dụng"
+ "Yêu cầu quyền"
+ "Đã phát hiện lớp phủ màn hình"
+ "Để thay đổi cài đặt quyền này, trước tiên bạn phải tắt lớp phủ màn hình từ Cài đặt > Ứng dụng"
+ "Mở cài đặt"
+ "Android Wear"
+ "Không hỗ trợ tác vụ Cài đặt/Gỡ cài đặt trên Wear."
+ "Chọn cho phép <b>%1$s</b> truy cập những gì"
+ "Đã cập nhật <b>%1$s</b>. Chọn cho phép ứng dụng này truy cập những gì."
+ "Hủy"
+ "Tiếp tục"
+ "Các quyền mới"
+ "Các quyền hiện tại"
+ "Đang sắp xếp ứng dụng…"
+ "Không xác định"
+ "Để bảo mật, máy tính bảng của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."
+ "Để bảo mật, TV của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."
+ "Để bảo mật, điện thoại của bạn không được phép cài đặt các ứng dụng không xác định từ nguồn này."
+ "Điện thoại và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với điện thoại của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."
+ "Máy tính bảng và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với máy tính bảng của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."
+ "TV và dữ liệu cá nhân của bạn dễ bị các ứng dụng không xác định tấn công hơn. Bằng cách cài đặt ứng dụng này, bạn đồng ý tự chịu trách nhiệm cho mọi hỏng hóc đối với TV của mình hoặc mất mát dữ liệu có thể phát sinh do sử dụng ứng dụng này."
+ "Tiếp tục"
+ "Cài đặt"
+ "Cài đặt/gỡ cài đặt ứng dụng Wear"
+
diff --git a/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml
new file mode 100644
index 0000000000000..b5beca32dd5a4
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w192dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+
+
+
+ 9.6dp
+ 19.2dp
+ 23.04dp
+ 28.8dp
+
diff --git a/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml
new file mode 100644
index 0000000000000..302d23eca825a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w205dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+
+
+
+ 10.25dp
+ 20.5dp
+ 24.6dp
+ 30.75dp
+
diff --git a/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml
new file mode 100644
index 0000000000000..937c5d004f4aa
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w225dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+
+
+
+ 11.25dp
+ 22.5dp
+ 27dp
+ 33.75dp
+
diff --git a/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml
new file mode 100644
index 0000000000000..b2ad3343a2070
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w228dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+
+
+
+ 11.4dp
+ 22.8dp
+ 27.36dp
+ 34.2dp
+
diff --git a/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml b/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml
new file mode 100644
index 0000000000000..ebc8c7526960a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-w240dp-watch/dimens_percent.xml
@@ -0,0 +1,21 @@
+
+
+
+ 12dp
+ 24dp
+ 28.8dp
+ 36dp
+
diff --git a/packages/PackageInstaller/res/values-watch/colors.xml b/packages/PackageInstaller/res/values-watch/colors.xml
new file mode 100644
index 0000000000000..81d0459cc7174
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/colors.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ #757575
+
diff --git a/packages/PackageInstaller/res/values-watch/integers.xml b/packages/PackageInstaller/res/values-watch/integers.xml
new file mode 100644
index 0000000000000..365ac3aba8053
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/integers.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
+ 13
+ 18
+
+
diff --git a/packages/PackageInstaller/res/values-watch/strings.xml b/packages/PackageInstaller/res/values-watch/strings.xml
new file mode 100644
index 0000000000000..f940eae6552e3
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/strings.xml
@@ -0,0 +1,38 @@
+
+
+
+
+
+ Deny, don\'t ask again
+
+
+
+ %1$s /
+ %2$s
+
+
+
+ Show system apps
+
+
+ Can\'t be changed
+
+
+ Yes
+
+
+ Cancel
+
diff --git a/packages/PackageInstaller/res/values-watch/styles.xml b/packages/PackageInstaller/res/values-watch/styles.xml
new file mode 100644
index 0000000000000..805d43d0c16a2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/styles.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values-watch/themes.xml b/packages/PackageInstaller/res/values-watch/themes.xml
new file mode 100644
index 0000000000000..3adcfc546c009
--- /dev/null
+++ b/packages/PackageInstaller/res/values-watch/themes.xml
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml b/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml
new file mode 100644
index 0000000000000..9b433709ae7e9
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "拒绝,不要再询问"
+ "您以后可以在“设置”>“应用”中更改此设置"
+ "%1$s / %2$s"
+ "显示系统应用"
+ "应用权限"
+ "应用权限"
+ "%1$s权限"
+ "其他权限"
+ "%1$s权限"
+
diff --git a/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml
new file mode 100644
index 0000000000000..252561940656c
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "拒绝,不要再询问"
+ "%1$s / %2$s"
+ "显示系统应用"
+ "无法更改"
+ "是"
+ "取消"
+
diff --git a/packages/PackageInstaller/res/values-zh-rCN/strings.xml b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
new file mode 100644
index 0000000000000..d559774d50bdc
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rCN/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "软件包安装程序"
+ "下一步"
+ "安装"
+ "完成"
+ "取消"
+ "正在安装..."
+ "正在安装%1$s…"
+ "应用安装完成。"
+ "要安装此应用吗?它将获得以下权限:"
+ "您要安装此应用吗?此应用不需要任何特殊权限。"
+ "您要安装此应用的新版本吗?您现有的数据不会丢失。更新后的应用将具备以下权限:"
+ "您要安装此内置应用的新版本吗?您现有的数据不会丢失。更新后的应用将具备以下权限:"
+ "是否要为这一现有应用安装更新?您现有的数据不会丢失,且安装过程无需任何特殊权限。"
+ "是否要为这一内置应用安装更新?您现有的数据不会丢失,且安装过程无需任何特殊权限。"
+ "应用未安装。"
+ "系统禁止安装该软件包。"
+ "应用未安装:软件包与现有软件包存在冲突。"
+ "应用未安装:应用与您的平板电脑不兼容。"
+ "此应用与您的电视不兼容。"
+ "应用未安装:应用与您的手机不兼容。"
+ "应用未安装:软件包似乎无效。"
+ "无法在您的平板电脑上安装“%1$s”。"
+ "无法将%1$s安装到您的电视上。"
+ "无法在您的手机上安装“%1$s”。"
+ "打开"
+ "您的管理员不允许安装来源不明的应用"
+ "该用户无法安装未知应用"
+ "此用户不能安装应用"
+ "确定"
+ "管理应用"
+ "没有存储空间"
+ "无法安装“%1$s”,请释放一些存储空间并重试。"
+ "未找到应用"
+ "未在已安装应用的列表中找到该应用。"
+ "不允许"
+ "当前用户无法执行这项卸载操作。"
+ "错误"
+ "无法卸载应用。"
+ "卸载应用"
+ "卸载更新"
+ "%1$s属于以下应用:"
+ "要卸载此应用吗?"
+ "是否要为""所有""用户卸载此应用?系统将为设备上的""所有""用户删除此应用及其数据。"
+ "您要为用户%1$s卸载此应用吗?"
+ "要将此应用替换为出厂版本吗?这样会移除所有数据。"
+ "要将此应用替换为出厂版本吗?这样会移除所有数据,并会影响此设备的所有用户(包括已设置工作资料的用户)。"
+ "进行中的卸载操作"
+ "失败的卸载操作"
+ "正在卸载..."
+ "正在卸载%1$s…"
+ "卸载完成。"
+ "已卸载%1$s"
+ "卸载失败。"
+ "卸载%1$s失败。"
+ "无法卸载正在使用中的设备管理应用"
+ "无法为%1$s卸载正在使用中的设备管理应用"
+ "这是部分用户或个人资料所需的应用;已为其他用户或个人资料卸载此应用"
+ "这是您的个人资料所需的应用,因此无法卸载。"
+ "这是您的设备管理员要求必须安装的应用,因此无法卸载。"
+ "管理设备管理应用"
+ "管理用户"
+ "无法卸载“%1$s”。"
+ "解析软件包时出现问题。"
+ "新权限"
+ "全部"
+ "隐私相关权限"
+ "设备相关权限"
+ "新版本不需要任何新的权限。"
+ "拒绝"
+ "详情"
+ "仍然拒绝"
+ "第 %1$s 项权限(共 %2$s 项)"
+ "要允许<b>%1$s</b>%2$s吗?"
+ "要一律允许<b>%1$s</b>%2$s吗?"
+ "仅限使用应用时"
+ "一律允许"
+ "拒绝,不要再询问"
+ "%1$d 项已停用"
+ "全部已停用"
+ "均未停用"
+ "允许"
+ "应用"
+ "应用权限"
+ "不再询问"
+ "没有权限"
+ "其他权限"
+ "打开应用信息"
+
+ 另外 %1$d 项
+ 另外 %1$d 项
+
+ "此应用专为旧版 Android 打造。拒绝权限可能会导致其无法正常运行。"
+ "执行未知操作"
+ "已授权 %1$d 个应用(共 %2$d 个)"
+ "显示系统应用"
+ "隐藏系统应用"
+ "没有应用"
+ "位置信息设置"
+ "“%1$s”是此设备的一个位置信息服务提供程序。您可以在位置信息设置中修改位置信息使用权。"
+ "如果您拒绝此权限,您设备的基本功能可能会无法正常使用。"
+ "依据政策强制执行"
+ "已根据政策停用后台访问权限"
+ "已根据政策启用后台访问权限"
+ "已根据政策启用前台访问权限"
+ "由管理员控制"
+
+
+
+ "一律允许"
+ "仅限使用应用时"
+ "永不"
+ "正在加载…"
+ "所有权限"
+ "其他应用功能"
+ "权限请求"
+ "检测到屏幕叠加层"
+ "要更改此权限设置,您必须首先在“设置”>“应用”中关闭屏幕叠加层"
+ "打开设置"
+ "Android Wear"
+ "Wear 不支持安装/卸载操作。"
+ "请选择要向<b>%1$s</b>授予哪些权限"
+ "<b>%1$s</b>已更新。请选择要向此应用授予哪些权限。"
+ "取消"
+ "继续"
+ "新权限"
+ "当前权限"
+ "正在准备安装应用…"
+ "未知"
+ "出于安全考虑,已禁止您的平板电脑安装来自此来源的未知应用。"
+ "出于安全考虑,已禁止您的电视安装来自此来源的未知应用。"
+ "出于安全考虑,已禁止您的手机安装来自此来源的未知应用。"
+ "来历不明的应用很可能会损害您的手机和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何手机损坏或数据丢失情况,您负有全部责任。"
+ "来历不明的应用很可能会损害您的平板电脑和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何平板电脑损坏或数据丢失情况,您负有全部责任。"
+ "来历不明的应用很可能会损害您的电视和个人数据。安装该应用即表示,您同意对于因使用该应用可能导致的任何电视损坏或数据丢失情况,您负有全部责任。"
+ "继续"
+ "设置"
+ "正在安装/卸载 Android Wear 应用"
+
diff --git a/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml b/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml
new file mode 100644
index 0000000000000..52c3d3007794a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "拒絕,不要再問我"
+ "您日後可以在 [設定] > [應用程式] 中變更這項設定"
+ "%1$s / %2$s"
+ "顯示系統應用程式"
+ "應用程式權限"
+ "應用程式權限"
+ "%1$s權限"
+ "其他權限"
+ "%1$s權限"
+
diff --git a/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml
new file mode 100644
index 0000000000000..6d0226f829800
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "拒絕,不要再問我"
+ "%1$s / %2$s"
+ "顯示系統應用程式"
+ "不可變更"
+ "是"
+ "取消"
+
diff --git a/packages/PackageInstaller/res/values-zh-rHK/strings.xml b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
new file mode 100644
index 0000000000000..0401eb1c93728
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rHK/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "程式安裝器"
+ "下一步"
+ "安裝"
+ "完成"
+ "取消"
+ "正在安裝..."
+ "正在安裝 %1$s…"
+ "已安裝應用程式。"
+ "您要安裝這個應用程式嗎?應用程式將取得以下存取權:"
+ "您要安裝這個應用程式嗎?應用程式不需任何特殊存取權。"
+ "您要為這個現有的應用程式安裝更新嗎?您的現有資料將會喪失,更新後的應用程式將取得以下存取權:"
+ "您要為這個內置的應用程式安裝更新嗎?您的現有資料將會喪失,更新後的應用程式將取得以下存取權:"
+ "您要為這個現有的應用程式安裝更新嗎?您不會遺失現有的資料,而應用程式無需任何特殊的存取權限。"
+ "您要為這個內置應用程式安裝更新嗎?您不會遺失現有的資料,而應用程式無需任何特殊的存取權限。"
+ "未安裝應用程式。"
+ "套件已遭封鎖,無法安裝。"
+ "套件與現有的套件發生衝突,無法安裝應用程式。"
+ "應用程式與平板電腦不兼容,無法安裝應用程式。"
+ "此應用程式與您的電視不相容。"
+ "應用程式與手機不兼容,無法安裝應用程式。"
+ "套件好像無效,無法安裝應用程式。"
+ "無法在您的平板電腦上安裝「%1$s」。"
+ "無法在您的電視上安裝 %1$s。"
+ "無法在您的手機上安裝「%1$s」。"
+ "開啟"
+ "您的管理員不允許安裝來自不明來源的應用程式"
+ "此使用者無法安裝不明的應用程式"
+ "此使用者無法安裝應用程式"
+ "確定"
+ "管理應用程式"
+ "空間不足"
+ "無法解除安裝「%1$s」。請先騰出一些空間,然後再試一次。"
+ "找不到應用程式"
+ "在已安裝的應用程式清單中找不到這個應用程式。"
+ "不允許"
+ "目前的使用者不允許執行這項解除安裝操作。"
+ "錯誤"
+ "應用程式無法解除安裝。"
+ "解除安裝應用程式"
+ "解除安裝更新"
+ "「%1$s」隸屬於以下應用程式:"
+ "您要解除安裝這個應用程式嗎?"
+ "您要為""所有""使用者解除安裝這個應用程式嗎?應用程式及其資料會從裝置上的""所有""使用者設定檔中移除。"
+ "您要為使用者 %1$s 解除安裝這個應用程式嗎?"
+ "要將此應用程式回復至原廠版本嗎?所有資料將會刪除。"
+ "要將此應用程式回復至原廠版本嗎?所有資料將會刪除,此裝置的所有使用者 (包括使用工作設定檔的使用者) 亦會受影響。"
+ "正在執行的解除安裝操作"
+ "失敗的解除安裝操作"
+ "正在解除安裝..."
+ "正在解除安裝「%1$s」…"
+ "完成解除安裝。"
+ "已解除安裝「%1$s」"
+ "解除安裝失敗。"
+ "解除安裝「%1$s」失敗。"
+ "無法解除安裝可用的裝置管理員應用程式"
+ "無法為%1$s解除安裝可用的裝置管理員應用程式"
+ "這是部分使用者或設定檔所需的應用程式,其他使用者或設定檔已解除安裝此應用程式"
+ "這是您設定檔所需的應用程式,因此無法解除安裝。"
+ "這是您的裝置管理員要求安裝的應用程式,因此無法解除安裝。"
+ "管理裝置管理員應用程式"
+ "管理使用者"
+ "無法解除安裝「%1$s」。"
+ "剖析套件時發生問題。"
+ "新增"
+ "全部"
+ "私隱權"
+ "裝置存取權"
+ "這項更新不需新權限。"
+ "拒絕"
+ "更多資訊"
+ "一律拒絕"
+ "第 %1$s 個 (共 %2$s 個)"
+ "允許「%1$s」%2$s嗎?"
+ "要一律允許「%1$s<b></b>」%2$s嗎?"
+ "只在使用應用程式時"
+ "一律"
+ "拒絕,不要再詢問"
+ "%1$d 個權限已停用"
+ "所有權限已停用"
+ "沒有權限已停用"
+ "允許"
+ "應用程式"
+ "應用程式權限"
+ "不要再問我"
+ "沒有權限"
+ "其他權限"
+ "打開應用程式資料"
+
+ 還有 %1$d 個
+ 還有 %1$d 個
+
+ "這個應用程式專為舊版本的 Android 設計。拒絕權限可能會導致它無法如預期 運作。"
+ "執行不明的操作"
+ "已允許 %1$d 個應用程式 (共 %2$d 個)"
+ "顯示系統"
+ "隱藏系統"
+ "沒有應用程式"
+ "位置設定"
+ "%1$s為此裝置提供位置資訊服務。您可以在位置設定中更改位置存取權。"
+ "如果您拒絕這個權限,您的裝置的基本功能可能無法正常運作。"
+ "由政策強制執行"
+ "已根據政策停用背景存取權"
+ "已根據政策啟用背景存取權"
+ "已根據政策啟用前景存取權"
+ "由管理員控制"
+
+
+
+ "一律"
+ "只在使用應用程式時"
+ "永不"
+ "正在載入…"
+ "所有權限"
+ "其他應用程式功能"
+ "權限要求"
+ "已偵測到螢幕重疊功能"
+ "如要變更此權限設定,請先前往 [設定] > [應用程式],以關閉螢幕重疊功能"
+ "開啟設定"
+ "Android Wear"
+ "Wear 不支援安裝/解除安裝操作。"
+ "選擇允許「%1$s」存取的內容"
+ "已更新「%1$s」。選擇允許此應用程式存取的內容。"
+ "取消"
+ "繼續"
+ "新權限"
+ "目前權限"
+ "正在準備安裝應用程式…"
+ "不明"
+ "為安全起見,您的平板電腦不得安裝此來源的不明應用程式。"
+ "為安全起見,您的電視不得安裝此來源的不明應用程式。"
+ "為安全起見,您的手機不得安裝此來源的不明應用程式。"
+ "來源不明的應用程式可能會侵害您的手機和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致手機損壞或資料遺失的責任。"
+ "來源不明的應用程式可能會侵害您的平板電腦和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致平板電腦損壞或資料遺失的責任。"
+ "來源不明的應用程式可能會侵害您的電視和個人資料。安裝此應用程式,即表示您同意承擔因使用這個應用程式而導致電視損壞或資料遺失的責任。"
+ "繼續"
+ "設定"
+ "正在安裝/解除安裝 Wear 應用程式"
+
diff --git a/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml b/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml
new file mode 100644
index 0000000000000..59266f9f011e2
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "拒絕且不要再詢問"
+ "你日後可在 [設定] > [應用程式] 中進行變更"
+ "%1$s/%2$s"
+ "顯示系統應用程式"
+ "應用程式權限"
+ "應用程式權限"
+ "%1$s權限"
+ "其他權限"
+ "%1$s權限"
+
diff --git a/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml b/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml
new file mode 100644
index 0000000000000..edaf7ead06494
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "拒絕且不要再詢問"
+ "%1$s / %2$s"
+ "顯示系統應用程式"
+ "無法變更"
+ "是"
+ "取消"
+
diff --git a/packages/PackageInstaller/res/values-zh-rTW/strings.xml b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000000000..db96924e851d1
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zh-rTW/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "程式安裝器"
+ "下一步"
+ "安裝"
+ "完成"
+ "取消"
+ "安裝中…"
+ "正在安裝「%1$s」…"
+ "已安裝應用程式。"
+ "你要安裝這個應用程式嗎?應用程式將取得以下權限:"
+ "你要安裝這個應用程式嗎?應用程式不需任何特殊權限。"
+ "你要為這個現有的應用程式安裝更新嗎?你的現有資料不會遺失,而更新後的應用程式將取得以下權限:"
+ "你要為這個內建的應用程式安裝更新嗎?你的現有資料不會遺失,而更新後的應用程式將取得以下權限:"
+ "你要為這個現有的應用程式安裝更新嗎?你不會遺失現有的資料,且應用程式不需任何特殊權限。"
+ "你要為這個現有的內建應用程式安裝更新嗎?你不會遺失現有的資料,且應用程式不需任何特殊權限。"
+ "未安裝應用程式。"
+ "這個套件已遭到封鎖,因此無法安裝。"
+ "應用程式套件與現有套件衝突,因此未能完成安裝。"
+ "應用程式與你的平板電腦不相容,因此未能完成安裝。"
+ "這個應用程式與你的電視不相容。"
+ "應用程式與你的手機不相容,因此未能完成安裝。"
+ "應用程式套件無效,因此未能完成安裝。"
+ "無法在你的平板電腦上安裝「%1$s」。"
+ "無法在你的電視上安裝「%1$s」。"
+ "無法在你的手機上安裝「%1$s」。"
+ "開啟"
+ "你的管理員不允許安裝來源不明的應用程式"
+ "這位使用者無法安裝不明的應用程式"
+ "這位使用者無法安裝應用程式"
+ "確定"
+ "管理應用程式"
+ "空間不足"
+ "無法安裝「%1$s」。請先釋出部分空間,然後再試一次。"
+ "找不到應用程式"
+ "在已安裝的應用程式清單中找不到這個應用程式。"
+ "不允許此操作"
+ "目前的使用者無法執行這項解除安裝作業。"
+ "發生錯誤"
+ "無法解除安裝應用程式。"
+ "解除安裝應用程式"
+ "解除安裝更新"
+ "「%1$s」屬於下列應用程式:"
+ "你要解除安裝這個應用程式嗎?"
+ "你要為""所有""使用者解除安裝這個應用程式嗎?該應用程式及其資料會從裝置上的""所有""使用者設定檔移除。"
+ "你要為使用者 %1$s 解除安裝這個應用程式嗎?"
+ "要將應用程式換成原廠版本嗎?這麼做會移除所有資料。"
+ "要將應用程式換成原廠版本嗎?這麼做會移除所有資料。凡是這個裝置的使用者 (包括設置工作資料夾的使用者),皆會受到影響。"
+ "執行中的解除安裝作業"
+ "失敗的解除安裝作業"
+ "解除安裝中…"
+ "正在解除安裝「%1$s」…"
+ "解除安裝完成。"
+ "已解除安裝「%1$s」"
+ "解除安裝失敗。"
+ "無法解除安裝「%1$s」。"
+ "無法解除安裝使用中的裝置管理員應用程式"
+ "無法為%1$s解除安裝使用中的裝置管理員應用程式"
+ "部分使用者或設定檔需要使用這個應用程式;已為其他使用者解除安裝"
+ "你的設定檔需要使用這個應用程式,因此無法解除安裝。"
+ "這是你的裝置管理員要求安裝的應用程式,因此無法解除安裝。"
+ "管理裝置管理員應用程式"
+ "管理使用者"
+ "無法解除安裝「%1$s」。"
+ "剖析套件時發生問題。"
+ "新增"
+ "全部"
+ "隱私權"
+ "裝置存取權"
+ "這項更新不需新權限。"
+ "拒絕"
+ "瞭解詳情"
+ "直接拒絕"
+ "%1$s/%2$s"
+ "要允許「%1$s」%2$s嗎?"
+ "要一律允許「%1$s」%2$s嗎?"
+ "僅限使用應用程式時"
+ "一律允許"
+ "拒絕且不要再詢問"
+ "已停用 %1$d 項權限"
+ "已停用所有權限"
+ "未停用任何權限"
+ "允許"
+ "應用程式"
+ "應用程式權限"
+ "不要再詢問"
+ "沒有權限"
+ "其他權限"
+ "開啟應用程式資訊"
+
+ 還有 %1$d 項
+ 還有 %1$d 項
+
+ "這個應用程式是為舊版 Android 所開發。拒絕授予權限可能導致應用程式無法正常運作。"
+ "執行不明的動作"
+ "已授權 %1$d 個應用程式 (共 %2$d 個)"
+ "顯示系統"
+ "隱藏系統"
+ "沒有應用程式"
+ "位置資訊設定"
+ "%1$s 是這台裝置的定位服務供應商。你可以在位置資訊設定中修改位置資訊存取權。"
+ "如果你拒絕這項權限,裝置的基本功能可能無法正常運作。"
+ "依據政策規定執行"
+ "已根據政策停用背景存取權"
+ "已根據政策啟用背景存取權"
+ "已根據政策啟用前景存取權"
+ "由管理員控管"
+
+
+
+ "一律允許"
+ "僅限使用應用程式時"
+ "永不"
+ "載入中…"
+ "所有權限"
+ "其他應用程式功能"
+ "權限要求"
+ "偵測到畫面重疊圖層"
+ "如要變更這項權限設定,你必須先依序前往 [設定] > [應用程式],關閉裝置畫面重疊圖層"
+ "開啟設定"
+ "Android Wear"
+ "Wear 不支援安裝及解除安裝操作。"
+ "選擇要將哪些存取權限授予「%1$s」"
+ "「%1$s」已更新。請選擇要將哪些存取權限授予這個應用程式。"
+ "取消"
+ "繼續"
+ "新權限"
+ "目前權限"
+ "正在啟動應用程式安裝程序…"
+ "不明"
+ "為了安全起見,你的平板電腦禁止安裝這個來源提供的不明應用程式。"
+ "為了安全起見,你的電視禁止安裝這個來源提供的不明應用程式。"
+ "為了安全起見,你的手機禁止安裝這個來源提供的不明應用程式。"
+ "來歷不明的應用程式可能會損害你的手機和個人資料。如因安裝及使用這個應用程式,導致你的手機受損或資料遺失,請自行負責。"
+ "來歷不明的應用程式可能會損害你的平板電腦和個人資料。如因安裝及使用這個應用程式,導致你的平板電腦受損或資料遺失,請自行負責。"
+ "來歷不明的應用程式可能會損害你的電視和個人資料。如因安裝及使用這個應用程式,導致你的電視受損或資料遺失,請自行負責。"
+ "繼續"
+ "設定"
+ "安裝/解除安裝 Wear 應用程式"
+
diff --git a/packages/PackageInstaller/res/values-zu-television/strings.xml b/packages/PackageInstaller/res/values-zu-television/strings.xml
new file mode 100644
index 0000000000000..a07ad2e97c523
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu-television/strings.xml
@@ -0,0 +1,28 @@
+
+
+
+
+ "Yenqaba futhi ungasabuzi"
+ "Ungashintsha lokhu kamuva kuzilungiselelo > izinhlelo zokusebenza"
+ "%1$s / %2$s"
+ "Bonisa izinhlelo zokusebenza zesistimu"
+ "Izimvume zohlelo lokusebenza"
+ "Izimvume zohlelo lokusebenza"
+ "%1$s izimvume"
+ "Izimvume ezingeziwe"
+ "%1$s izimvume"
+
diff --git a/packages/PackageInstaller/res/values-zu-watch/strings.xml b/packages/PackageInstaller/res/values-zu-watch/strings.xml
new file mode 100644
index 0000000000000..38fe1c8eec3f0
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu-watch/strings.xml
@@ -0,0 +1,25 @@
+
+
+
+
+ "Yenqaba, ungangibuzi futhi"
+ "%1$s / %2$s"
+ "Bonisa izinhlelo zokusebenza zesistimu"
+ "Akukwazi ukushintshwa"
+ "Yebo"
+ "Khansela"
+
diff --git a/packages/PackageInstaller/res/values-zu/strings.xml b/packages/PackageInstaller/res/values-zu/strings.xml
new file mode 100644
index 0000000000000..6ece4793b175a
--- /dev/null
+++ b/packages/PackageInstaller/res/values-zu/strings.xml
@@ -0,0 +1,156 @@
+
+
+
+
+ "Isifaki sephakheji"
+ "Okulandelayo"
+ "Faka"
+ "Kwenziwe"
+ "Khansela"
+ "Iyafaka..."
+ "Ifaka i-%1$s…"
+ "I-App ifakiwe."
+ "Ngabe ufuna ukufaka lolu hlelo lokusebenza? Lizothola ukufinyelela ku:"
+ "Ngabe ufuna ukufaka lolu hlelo lokusebenza? Alidingi ukufinyelela okukhethekile."
+ "Ngabe ufuna ukufaka isibuyekezo ohlelweni lokusebenza olukhona? Idatha yakho ekhona izolahleka. Uhlelo lokusebenza olubuyekeziwe lizothola ukufinyelela ku:"
+ "Ngabe ufuna ukufaka isibuyekezo kulolu hlelo lokusebenza olakhelwe phakathi? Idatha yakho ekhona izolahleka. Uhlelo lokusebenza olubuyekeziwe luzothola ukufinyelela ku:"
+ "Ingabe ufuna ukufaka isibuyekezo kulolu hlelo lokusebenza olukhona? Idatha yakho ekhona ngeke ilahleke. Akudingi ukufinyelela okukhethekile."
+ "Ungabe ufuna ukukhipha isibuyekezo kulolu hlelo lokusebenza olakhelwe ngaphakathi? Idatha yakho ekhona ngeke ilahleke. Akudingi ukufinyelela okukhethekile."
+ "I-app ayifakiwe."
+ "Iphakheji livinjiwe kusukela ekufakweni."
+ "Uhlelo lokusebenza alufakiwe njengoba ukuphakheja kushayisana nephakheji elikhona."
+ "Uhlelo lokusebenza alufakiwe njengoba uhlelo lokusebenza lungahambisani nethebulethi yakho."
+ "Lolu hlelo lokusebenza aluhambisani ne-TV yakho."
+ "Uhlelo lokusebenza alufakiwe njengoba uhlelo lokusebenza lungahambisani nefoni yakho."
+ "Uhlelo lokusebenza alufakiwe njengoba iphakheji ibonakala ingavumelekile."
+ "I-%1$s ayikwazanga ukufakwa kuthebhulethi."
+ "I-%1$s ayikwazanga ukufakwa ku-TV yakho."
+ "I-%1$s ayikwazanga ukufakwa efonini."
+ "Vula"
+ "Umlawuli wakho akavumeli ukufakwa kwezinhlelo zokusebenza ezitholwe kusukela kumithombo engaziwa"
+ "Izinhlelo zokusebenza ezingaziwa azikwazi ukufakwa ilo msebenzisi"
+ "Lo msebenzisi akavunyelwe ukufaka izinhlelo zokusebenza"
+ "KULUNGILE"
+ "Phatha izinhlelo zokusebenza"
+ "Iphelelwe yisikhala"
+ "I-%1$s ayikwazanga ukufakwa. Khulula isikhala bese uzama futhi."
+ "I-App ayitholakalanga"
+ "Uhlelo lokusebenza alutholakalanga ohlwini lwezinhlelo zokusebenza ezifakiwe."
+ "Akuvumelekile"
+ "Umsebenzisi wamanje akavunyelwe ukwenza lokhu kukhipha."
+ "Iphutha"
+ "Amafu ohlelo lokusebenza angakhishwa."
+ "Khipha i-app"
+ "Khipha isibuyekezo"
+ "I-%1$s ingxenye yohlelo lokusebenza olulandelayo:"
+ "Ufuna ukukhipha le-app?"
+ "Ingabe ufuna ukukhipha lolu hlelo lokusebenza kubo ""bonke"" abasebenzisi? Uhlelo lokusebenza nedatha yalo kuzosuswa kubo ""bonke"" abasebenzisi kudivayisi."
+ "Ingabe ufuna ukukhiphela lolu hlelo lokusebenza kumsebenzisi ongu-%1$s?"
+ "Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa."
+ "Shintshanisa lolu hlelo lokusebenza ngenguqulo yasekuqaleni? Yonke idatha izosuswa. Lokhu kuthinta bonke abasebenzisi bale divayisi, abafaka labo abanamaphrofayela wokusebenza."
+ "Ukukhishwa okuqhubekayo"
+ "Ukukhishwa okuhlulekile"
+ "Iyakhipha..."
+ "Ikhipha i-%1$s…"
+ "Ukukhipha kuqedile"
+ "Kukhishwe i-%1$s"
+ "Ukukhipha akuphumelelanga."
+ "Ukukhipha i-%1$s akuphumelele."
+ "Ayikwazi ukukhipha uhlelo lokusebenza lomlawuli ledivayisi esebenzayo"
+ "Ayikwazi ukukhipha uhlelo lokusebenza lomlawuli ledivayisi esebenzayo lika-%1$s"
+ "Lolu hlelo lokusebenza luyadingeka kwabanye abasebenzisi noma amaphrofayela futhi lukhishelwe abanye"
+ "Lolu hlelo lokusebenza ludingelwa iphrofayela yakho futhi alikwazi ukukhishwa."
+ "Lolu hlelo lokusebenza ludingwa umlawuli wedivayisi yakho futhi alukwazi ukukhishwa."
+ "Phatha izinhlelo zokusebenza zedivayisi yomlawuli"
+ "Phatha abasebenzisi"
+ "I-%1$s ayikwazanga ukukhishwa"
+ "Kube nenkinga yokwehlukanisa iphakheji."
+ "Okusha"
+ "Konke"
+ "Ubumfihlo"
+ "Ukufinyelela kwedivayisi"
+ "Lesi sibuyekezo asidingi zimvume."
+ "Phika"
+ "Olunye ulwazi"
+ "Yenqaba noma kunjalo"
+ "%1$s kokungu-%2$s"
+ "Vumela i-<b>%1$s</b> ukuthi %2$s?"
+ "Njalo vumela i-<b>%1$s</b> ukwenza %2$s?"
+ "Kuphela ngenkathi usebenzisa uhlelo lokusebenza"
+ "Njalo"
+ "Yenqaba futhi ungasabuzi"
+ "%1$d kukhutshaziwe"
+ "konke kukhutshaziwe"
+ "Lutho olukhutshaziwe"
+ "Vumela"
+ "Izinhlelo zokusebenza"
+ "Izimvume zohlelo lokusebenza"
+ "Ungaphindi ubuze"
+ "Akukho zimvume"
+ "Izimvume ezingeziwe"
+ "Vula ulwazi lohlelo lokusebenza"
+
+ %1$d okuningi
+ %1$d okuningi
+
+ "Lolu hlelo lokusebenza ludizayinelwe inguqulo endala ye-Android. Ukwala imvume kungalibangela ukuthi lingasasebenzi njengoba kuhlosiwe."
+ "Yenza isenzo esingaziwa"
+ "%1$d kuzinhlelo zokusebenza ezingu-%2$d ezivunyelwe"
+ "Bonisa isistimu"
+ "Fihla isistimu"
+ "Azikho izinhlelo zokusebenza"
+ "Izilungiselelo Zendawo"
+ "%1$s ingumhlinzeki wamasevisi wendawo kule divayisi. Ukufinyelela kwendawo kungashintshwa kusuka kuzilungiselelo zendawo."
+ "Uma unqabela le mvume, izici eziyisisekelo zedivayisi yakho zingahle zingasasebenzi njengoba zihlosiwe."
+ "Isetshenziswe yinqubomgomo"
+ "Ukufinyelela kwangemuva kukhutshazwe inqubomgomo"
+ "Ukufinyelela kwangemuva kunikwe amandla ngenqubomgomo"
+ "Ukufinyelela kwangaphambili kunikwe amandla ngenqubomgomo"
+ "Kulawulwa umqondisi"
+
+
+
+ "Njalo"
+ "Kuphela ngenkathi usebenzisa uhlelo lokusebenza"
+ "Soze"
+ "Iyalayisha..."
+ "Zonke izimvume"
+ "Amanye amakhono wohlelo lokusebenza"
+ "Isicelo semvume"
+ "Kutholwe imbondela yesikrini"
+ "Ukuze uguqule lesi silungiselelo semvume, kuzomele uqale uvale imbondela yesikrini kusukela ku-Izilungiselelo > Izinhlelo zokusebenza"
+ "Vula izilungiselelo"
+ "I-Android Wear"
+ "Izenzo zokufaka/ukukhipha azisekelwe ku-Wear."
+ "Khetha ukuthi uzovumela ini ukuthi i-<b>%1$s</b> ifinyelele kuyo"
+ "I-<b>%1$s</b> ibuyekeziwe. Khetha ukuthi uzovumela ini ukuthi ifinyelelwe ilolu hlelo lokusebenza."
+ "Khansela"
+ "Qhubeka"
+ "Izimvume ezintsha"
+ "Izimvume zamanje"
+ "Ifaka kusiteji uhlelo lokusebenza…"
+ "Akwaziwa"
+ "Ukuze uvikelwe, ithebulethi yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza ezingaziwa kusukela kulo mthombo."
+ "Ukuze uvikelwe, i-TV yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza ezingaziwa kusukela kulo mthombo."
+ "Ukuze uvikelwe, ifoni yakho ayivunyelwe ukuthi ifake izinhlelo zokusebenza kusukela kulo mthombo."
+ "Idatha yakho yefoni neyohlelo lwakho lokusebenza isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kufoni yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."
+ "Ithebulethi yakho nedatha yomuntu siqu zisengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala kuthebulethi yakho noma ukulahleka kwedatha okungabangelwa ukusetshenziswa kwayo."
+ "Idatha yakho ye-TV neyomuntu siqu isengcupheni kakhulu ekuhlaselweni izinhlelo zokusebenza ezingaziwa. Ngokufaka lolu hlelo lokusebenza, uyavuma ukuthi unesibopho sanoma ikuphi ukonakala ku-TV yakho noma ukulahlekelwa kwedatha okungabangelwa ukusetshenziswa kwayo."
+ "Qhubeka"
+ "Izilungiselelo"
+ "Ifaka/ikhipha izinhlelo zokusebenza ze-wear"
+
diff --git a/packages/PackageInstaller/res/values/attrs.xml b/packages/PackageInstaller/res/values/attrs.xml
new file mode 100644
index 0000000000000..e220f4c88edfc
--- /dev/null
+++ b/packages/PackageInstaller/res/values/attrs.xml
@@ -0,0 +1,35 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values/colors.xml b/packages/PackageInstaller/res/values/colors.xml
new file mode 100644
index 0000000000000..01a0c9a844cbd
--- /dev/null
+++ b/packages/PackageInstaller/res/values/colors.xml
@@ -0,0 +1,20 @@
+
+
+
+
+ #C8CCCE
+
diff --git a/packages/PackageInstaller/res/values/dimens.xml b/packages/PackageInstaller/res/values/dimens.xml
new file mode 100644
index 0000000000000..112723f48217c
--- /dev/null
+++ b/packages/PackageInstaller/res/values/dimens.xml
@@ -0,0 +1,44 @@
+
+
+
+
+
+ 72dp
+ 16dp
+
+
+ 16dp
+ 16dp
+
+ 384dp
+
+ 576dp
+ 48dp
+ 16dp
+ 280dp
+ 2dp
+ 36sp
+ 140dp
+
+ 18sp
+ 14sp
+ 4dp
+
+ 8dp
+
+ 8dp
+ 24dp
+
diff --git a/packages/PackageInstaller/res/values/strings.xml b/packages/PackageInstaller/res/values/strings.xml
new file mode 100644
index 0000000000000..23eb8657e2997
--- /dev/null
+++ b/packages/PackageInstaller/res/values/strings.xml
@@ -0,0 +1,424 @@
+
+
+
+
+
+ Package installer
+
+
+ Install
+
+ Done
+
+ Cancel
+
+ Installing\u2026
+
+ Installing %1$s\u2026
+
+ App installed.
+
+
+ Do you want to install this application?
+
+ Do you want to install an update
+ to this existing application? Your existing data will not
+ be lost. The updated application will get access to:
+
+ Do you want to install an update
+ to this built-in application? Your existing data will not
+ be lost. The updated application will get access to:
+
+ App not installed.
+
+ The package was blocked from being installed.
+
+ App not installed as package conflicts with an existing package.
+
+ App not installed as app isn\'t
+ compatible with your tablet.
+
+ This app isn\'t
+ compatible with your TV.
+
+ App not installed as app isn\'t
+ compatible with your phone.
+
+ App not installed as package appears to be invalid.
+
+ %1$s couldn\'t be installed on your tablet.
+
+ %1$s couldn\'t be installed on your TV.
+
+ %1$s couldn\'t be installed on your phone.
+ Open
+
+
+ Your admin doesn\'t allow installation of apps
+ obtained from unknown sources
+
+ Unknown apps can\'t be installed by this
+ user
+
+ This user is not allowed to install apps
+
+
+ OK
+
+ Manage apps
+
+ Out of space
+
+ %1$s couldn\'t be installed. Free up some space and try again.
+
+
+ App not found
+
+ The app wasn\'t found in the list of installed apps.
+
+ Not allowed
+
+ The current user is not allowed to perform this uninstallation.
+
+ Error
+
+ App could not be uninstalled.
+
+ Uninstall app
+
+ Uninstall update
+
+ %1$s is part of the following app:
+
+ Do you want to uninstall this app?
+
+ Do you want to uninstall this app for all
+ users? The application and its data will be removed from all users on the device.
+
+ Do you want to uninstall this app for the user %1$s?
+
+ Replace this app with the factory version? All data will be removed.
+
+ Replace this app with the factory version? All data will be removed. This affects all users of this device, including those with work profiles.
+
+
+ Running uninstalls
+
+ Failed uninstalls
+
+
+ Uninstalling\u2026
+
+ Uninstalling %1$s\u2026
+
+ Uninstall finished.
+
+ Uninstalled %1$s
+
+ Uninstall unsuccessful.
+
+ Uninstalling %1$s unsuccessful.
+
+ Can\'t uninstall active device admin
+ app
+
+ Can\'t uninstall active device
+ admin app for %1$s
+
+ This app is required for some users or
+ profiles and was uninstalled for others
+
+ This app is needed for
+ your profile and can\'t be uninstalled.
+
+ This app is required
+ by your device administrator and can\'t be uninstalled.
+
+ Manage device admin apps
+
+ Manage users
+
+ %1$s couldn\'t be uninstalled.
+
+
+ There was a problem parsing the package.
+
+
+ New
+
+ All
+
+ Privacy
+
+ Device Access
+
+
+ This update requires no new permissions.
+
+
+ Deny
+
+
+ More info
+
+
+ Deny anyway
+
+
+
+ %1$s of
+ %2$s
+
+
+ Allow
+ <b>%1$s</b> to
+ %2$s?
+
+
+ Always allow
+ <b>%1$s</b> to
+ %2$s?
+
+
+ Only while using app
+
+
+ Always
+
+
+ Deny and don\u2019t ask again
+
+
+ %1$d disabled
+
+
+ all disabled
+
+
+ none disabled
+
+
+
+
+ Allow
+
+
+ Apps
+
+
+ App permissions
+
+ Don\'t ask again
+
+
+ No permissions
+
+
+ Additional permissions
+
+
+ Open app info
+
+
+
+ %1$d more
+ %1$d more
+
+
+
+ This app was designed for an older version of Android. Denying permission may cause it to no longer function as intended.
+
+
+ perform an unknown action
+
+
+ %1$d of %2$d apps allowed
+
+
+ Show system
+
+ Hide system
+
+
+ No apps
+
+
+ Location Settings
+
+
+ %1$s is a provider of location services for this device. Location access can be modified from location settings.
+
+
+ If you deny this permission, basic features of your device may no longer function as intended.
+
+
+ Enforced by policy
+
+
+ Background access disabled by policy
+
+
+ Background access enabled by policy
+
+
+ Foreground access enabled by policy
+
+
+ Controlled by admin
+
+
+ @string/permission_access_always
+ @string/permission_access_only_foreground
+ @string/permission_access_never
+
+
+
+ Always
+
+
+ Only while using app
+
+
+ Never
+
+
+ Loading\u2026
+
+
+ All permissions
+
+ Other app capabilities
+
+
+ Permission request
+
+
+ Screen overlay detected
+
+
+ To change this permission setting, you first have to turn off the screen overlay from Settings \u003e Apps
+
+
+ Open settings
+
+
+ Android Wear
+
+ Install/Uninstall actions not supported on Wear.
+
+
+
+
+ Choose what to allow
+ <b>%1$s</b> to access
+
+
+
+ <b>%1$s</b> has been updated.
+ Choose what to allow this app to access.
+
+
+ Cancel
+
+
+ Continue
+
+
+ New permissions
+
+
+ Current permissions
+
+
+ Staging app…
+
+
+ Unknown
+
+
+
+
+
+ For your security, your tablet is not allowed to install unknown apps from this source.
+
+
+ For your security, your TV is not allowed to install unknown apps from this source.
+
+
+ For your security, your phone is not allowed to install unknown apps from this source.
+
+
+
+ Your phone and personal data are more vulnerable
+ to attack by unknown apps. By installing this app, you
+ agree that you are responsible for any damage to your
+ phone or loss of data that may result from its use.
+
+
+
+
+ Your tablet and personal data are more vulnerable
+ to attack by unknown apps. By installing this app, you
+ agree that you are responsible for any damage to your
+ tablet or loss of data that may result from its use.
+
+
+
+
+ Your TV and personal data are more vulnerable
+ to attack by unknown apps. By installing this app, you
+ agree that you are responsible for any damage to your
+ TV or loss of data that may result from its use.
+
+
+
+ Continue
+
+
+ Settings
+
+
+ Installing/uninstalling wear apps
+
+
diff --git a/packages/PackageInstaller/res/values/styles.xml b/packages/PackageInstaller/res/values/styles.xml
new file mode 100755
index 0000000000000..f79f98feafdc4
--- /dev/null
+++ b/packages/PackageInstaller/res/values/styles.xml
@@ -0,0 +1,39 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/values/themes.xml b/packages/PackageInstaller/res/values/themes.xml
new file mode 100644
index 0000000000000..3ddb6a7a9444d
--- /dev/null
+++ b/packages/PackageInstaller/res/values/themes.xml
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/xml-watch/watch_permissions.xml b/packages/PackageInstaller/res/xml-watch/watch_permissions.xml
new file mode 100644
index 0000000000000..8ec8d196fe001
--- /dev/null
+++ b/packages/PackageInstaller/res/xml-watch/watch_permissions.xml
@@ -0,0 +1,22 @@
+
+
+
+
+
diff --git a/packages/PackageInstaller/res/xml/all_permissions.xml b/packages/PackageInstaller/res/xml/all_permissions.xml
new file mode 100644
index 0000000000000..5f7f847bafbd7
--- /dev/null
+++ b/packages/PackageInstaller/res/xml/all_permissions.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/AcceptDenyDialog.java b/packages/PackageInstaller/src/android/support/wearable/view/AcceptDenyDialog.java
new file mode 100644
index 0000000000000..f2129b8f4b177
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/AcceptDenyDialog.java
@@ -0,0 +1,182 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package androidx.wear.ble.view;
+
+import android.annotation.TargetApi;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import androidx.annotation.StyleRes;
+import android.view.View;
+import android.widget.ImageButton;
+import android.widget.ImageView;
+import android.widget.Space;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+
+/**
+ * A dialog to display a title, a message, and/or an icon with a positive and a negative button.
+ *
+ *
The buttons are hidden away unless there is a listener attached to the button. Since there's
+ * no click listener attached by default, the buttons are hidden be default.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class AcceptDenyDialog extends Dialog {
+ /** Icon at the top of the dialog. */
+ protected ImageView mIcon;
+ /** Title at the top of the dialog. */
+ protected TextView mTitle;
+ /** Message content of the dialog. */
+ protected TextView mMessage;
+ /** Panel containing the buttons. */
+ protected View mButtonPanel;
+ /** Positive button in the button panel. */
+ protected ImageButton mPositiveButton;
+ /** Negative button in the button panel. */
+ protected ImageButton mNegativeButton;
+ /**
+ * Click listener for the positive button. Positive button should hide if this is null
+ * .
+ */
+ protected DialogInterface.OnClickListener mPositiveButtonListener;
+ /**
+ * Click listener for the negative button. Negative button should hide if this is null
+ * .
+ */
+ protected DialogInterface.OnClickListener mNegativeButtonListener;
+ /** Spacer between the positive and negative button. Hidden if one button is hidden. */
+ protected View mSpacer;
+
+ private final View.OnClickListener mButtonHandler = (v) -> {
+ if (v == mPositiveButton && mPositiveButtonListener != null) {
+ mPositiveButtonListener.onClick(this, DialogInterface.BUTTON_POSITIVE);
+ dismiss();
+ } else if (v == mNegativeButton && mNegativeButtonListener != null) {
+ mNegativeButtonListener.onClick(this, DialogInterface.BUTTON_NEGATIVE);
+ dismiss();
+ }
+ };
+
+ public AcceptDenyDialog(Context context) {
+ this(context, 0 /* use default context theme */);
+ }
+
+ public AcceptDenyDialog(Context context, @StyleRes int themeResId) {
+ super(context, themeResId);
+
+ setContentView(R.layout.accept_deny_dialog);
+
+ mTitle = (TextView) findViewById(android.R.id.title);
+ mMessage = (TextView) findViewById(android.R.id.message);
+ mIcon = (ImageView) findViewById(android.R.id.icon);
+ mPositiveButton = (ImageButton) findViewById(android.R.id.button1);
+ mPositiveButton.setOnClickListener(mButtonHandler);
+ mNegativeButton = (ImageButton) findViewById(android.R.id.button2);
+ mNegativeButton.setOnClickListener(mButtonHandler);
+ mSpacer = (Space) findViewById(R.id.spacer);
+ mButtonPanel = findViewById(R.id.buttonPanel);
+ }
+
+ public ImageButton getButton(int whichButton) {
+ switch (whichButton) {
+ case DialogInterface.BUTTON_POSITIVE:
+ return mPositiveButton;
+ case DialogInterface.BUTTON_NEGATIVE:
+ return mNegativeButton;
+ default:
+ return null;
+ }
+ }
+
+ public void setIcon(Drawable icon) {
+ mIcon.setVisibility(icon == null ? View.GONE : View.VISIBLE);
+ mIcon.setImageDrawable(icon);
+ }
+
+ /**
+ * @param resId the resourceId of the drawable to use as the icon or 0 if you don't want an icon.
+ */
+ public void setIcon(int resId) {
+ mIcon.setVisibility(resId == 0 ? View.GONE : View.VISIBLE);
+ mIcon.setImageResource(resId);
+ }
+
+ /** @param message the content message text of the dialog. */
+ public void setMessage(CharSequence message) {
+ mMessage.setText(message);
+ mMessage.setVisibility(message == null ? View.GONE : View.VISIBLE);
+ }
+
+ /** @param title the title text of the dialog. */
+ @Override
+ public void setTitle(CharSequence title) {
+ mTitle.setText(title);
+ }
+
+ /**
+ * Sets a click listener for a button.
+ *
+ *
Will hide button bar if all buttons are hidden (i.e. their click listeners are null
+ * ).
+ *
+ * @param whichButton {@link DialogInterface.BUTTON_POSITIVE} or {@link
+ * DialogInterface.BUTTON_NEGATIVE}
+ * @param listener the listener to set for the button. Hide button if null.
+ */
+ public void setButton(int whichButton, DialogInterface.OnClickListener listener) {
+ switch (whichButton) {
+ case DialogInterface.BUTTON_POSITIVE:
+ mPositiveButtonListener = listener;
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ mNegativeButtonListener = listener;
+ break;
+ default:
+ return;
+ }
+
+ mSpacer.setVisibility(mPositiveButtonListener == null || mNegativeButtonListener == null
+ ? View.GONE : View.INVISIBLE);
+ mPositiveButton.setVisibility(
+ mPositiveButtonListener == null ? View.GONE : View.VISIBLE);
+ mNegativeButton.setVisibility(
+ mNegativeButtonListener == null ? View.GONE : View.VISIBLE);
+ mButtonPanel.setVisibility(
+ mPositiveButtonListener == null && mNegativeButtonListener == null
+ ? View.GONE : View.VISIBLE);
+ }
+
+ /**
+ * Convenience method for setButton(DialogInterface.BUTTON_POSITIVE, listener).
+ *
+ * @param listener the listener for the positive button.
+ */
+ public void setPositiveButton(DialogInterface.OnClickListener listener) {
+ setButton(DialogInterface.BUTTON_POSITIVE, listener);
+ }
+
+ /**
+ * Convenience method for setButton(DialogInterface.BUTTON_NEGATIVE, listener).
+ *
+ * @param listener the listener for the positive button.
+ */
+ public void setNegativeButton(DialogInterface.OnClickListener listener) {
+ setButton(DialogInterface.BUTTON_NEGATIVE, listener);
+ }
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/CircledImageView.java b/packages/PackageInstaller/src/android/support/wearable/view/CircledImageView.java
new file mode 100644
index 0000000000000..0ae344663bf20
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/CircledImageView.java
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 androidx.wear.ble.view;
+
+import android.animation.ArgbEvaluator;
+import android.animation.ValueAnimator;
+import android.animation.ValueAnimator.AnimatorUpdateListener;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.content.res.ColorStateList;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.graphics.Color;
+import android.graphics.Paint;
+import android.graphics.Paint.Style;
+import android.graphics.RadialGradient;
+import android.graphics.Rect;
+import android.graphics.RectF;
+import android.graphics.Shader;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.AttributeSet;
+import android.view.View;
+
+import java.util.Objects;
+import com.android.packageinstaller.R;
+
+import com.android.packageinstaller.R;
+
+/**
+ * An image view surrounded by a circle.
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class CircledImageView extends View {
+
+ private static final ArgbEvaluator ARGB_EVALUATOR = new ArgbEvaluator();
+
+ private Drawable mDrawable;
+
+ private final RectF mOval;
+ private final Paint mPaint;
+
+ private ColorStateList mCircleColor;
+
+ private float mCircleRadius;
+ private float mCircleRadiusPercent;
+
+ private float mCircleRadiusPressed;
+ private float mCircleRadiusPressedPercent;
+
+ private float mRadiusInset;
+
+ private int mCircleBorderColor;
+
+ private float mCircleBorderWidth;
+ private float mProgress = 1f;
+ private final float mShadowWidth;
+
+ private float mShadowVisibility;
+ private boolean mCircleHidden = false;
+
+ private float mInitialCircleRadius;
+
+ private boolean mPressed = false;
+
+ private boolean mProgressIndeterminate;
+ private ProgressDrawable mIndeterminateDrawable;
+ private Rect mIndeterminateBounds = new Rect();
+ private long mColorChangeAnimationDurationMs = 0;
+
+ private float mImageCirclePercentage = 1f;
+ private float mImageHorizontalOffcenterPercentage = 0f;
+ private Integer mImageTint;
+
+ private final Drawable.Callback mDrawableCallback = new Drawable.Callback() {
+ @Override
+ public void invalidateDrawable(Drawable drawable) {
+ invalidate();
+ }
+
+ @Override
+ public void scheduleDrawable(Drawable drawable, Runnable runnable, long l) {
+ // Not needed.
+ }
+
+ @Override
+ public void unscheduleDrawable(Drawable drawable, Runnable runnable) {
+ // Not needed.
+ }
+ };
+
+ private int mCurrentColor;
+
+ private final AnimatorUpdateListener mAnimationListener = new AnimatorUpdateListener() {
+ @Override
+ public void onAnimationUpdate(ValueAnimator animation) {
+ int color = (int) animation.getAnimatedValue();
+ if (color != CircledImageView.this.mCurrentColor) {
+ CircledImageView.this.mCurrentColor = color;
+ CircledImageView.this.invalidate();
+ }
+ }
+ };
+
+ private ValueAnimator mColorAnimator;
+
+ public CircledImageView(Context context) {
+ this(context, null);
+ }
+
+ public CircledImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public CircledImageView(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+
+ TypedArray a = getContext().obtainStyledAttributes(attrs, R.styleable.CircledImageView);
+ mDrawable = a.getDrawable(R.styleable.CircledImageView_android_src);
+
+ mCircleColor = a.getColorStateList(R.styleable.CircledImageView_circle_color);
+ if (mCircleColor == null) {
+ mCircleColor = ColorStateList.valueOf(android.R.color.darker_gray);
+ }
+
+ mCircleRadius = a.getDimension(
+ R.styleable.CircledImageView_circle_radius, 0);
+ mInitialCircleRadius = mCircleRadius;
+ mCircleRadiusPressed = a.getDimension(
+ R.styleable.CircledImageView_circle_radius_pressed, mCircleRadius);
+ mCircleBorderColor = a.getColor(
+ R.styleable.CircledImageView_circle_border_color, Color.BLACK);
+ mCircleBorderWidth = a.getDimension(R.styleable.CircledImageView_circle_border_width, 0);
+
+ if (mCircleBorderWidth > 0) {
+ mRadiusInset += mCircleBorderWidth;
+ }
+
+ float circlePadding = a.getDimension(R.styleable.CircledImageView_circle_padding, 0);
+ if (circlePadding > 0) {
+ mRadiusInset += circlePadding;
+ }
+ mShadowWidth = a.getDimension(R.styleable.CircledImageView_shadow_width, 0);
+
+ mImageCirclePercentage = a.getFloat(
+ R.styleable.CircledImageView_image_circle_percentage, 0f);
+
+ mImageHorizontalOffcenterPercentage = a.getFloat(
+ R.styleable.CircledImageView_image_horizontal_offcenter_percentage, 0f);
+
+ if (a.hasValue(R.styleable.CircledImageView_image_tint)) {
+ mImageTint = a.getColor(R.styleable.CircledImageView_image_tint, 0);
+ }
+
+ mCircleRadiusPercent = a.getFraction(R.styleable.CircledImageView_circle_radius_percent,
+ 1, 1, 0f);
+
+ mCircleRadiusPressedPercent = a.getFraction(
+ R.styleable.CircledImageView_circle_radius_pressed_percent, 1, 1,
+ mCircleRadiusPercent);
+
+ a.recycle();
+
+ mOval = new RectF();
+ mPaint = new Paint();
+ mPaint.setAntiAlias(true);
+
+ mIndeterminateDrawable = new ProgressDrawable();
+ // {@link #mDrawableCallback} must be retained as a member, as Drawable callback
+ // is held by weak reference, we must retain it for it to continue to be called.
+ mIndeterminateDrawable.setCallback(mDrawableCallback);
+
+ setWillNotDraw(false);
+
+ setColorForCurrentState();
+ }
+
+ public void setCircleHidden(boolean circleHidden) {
+ if (circleHidden != mCircleHidden) {
+ mCircleHidden = circleHidden;
+ invalidate();
+ }
+ }
+
+
+ @Override
+ protected boolean onSetAlpha(int alpha) {
+ return true;
+ }
+
+ @Override
+ protected void onDraw(Canvas canvas) {
+ int paddingLeft = getPaddingLeft();
+ int paddingTop = getPaddingTop();
+
+
+ float circleRadius = mPressed ? getCircleRadiusPressed() : getCircleRadius();
+ if (mShadowWidth > 0 && mShadowVisibility > 0) {
+ // First let's find the center of the view.
+ mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
+ // Having the center, lets make the shadow start beyond the circled and possibly the
+ // border.
+ final float radius = circleRadius + mCircleBorderWidth +
+ mShadowWidth * mShadowVisibility;
+ mPaint.setColor(Color.BLACK);
+ mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha()));
+ mPaint.setStyle(Style.FILL);
+ // TODO: precalc and pre-allocate this
+ mPaint.setShader(new RadialGradient(mOval.centerX(), mOval.centerY(), radius,
+ new int[]{Color.BLACK, Color.TRANSPARENT}, new float[]{0.6f, 1f},
+ Shader.TileMode.MIRROR));
+ canvas.drawCircle(mOval.centerX(), mOval.centerY(), radius, mPaint);
+ mPaint.setShader(null);
+ }
+ if (mCircleBorderWidth > 0) {
+ // First let's find the center of the view.
+ mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
+ // Having the center, lets make the border meet the circle.
+ mOval.set(mOval.centerX() - circleRadius, mOval.centerY() - circleRadius,
+ mOval.centerX() + circleRadius, mOval.centerY() + circleRadius);
+ mPaint.setColor(mCircleBorderColor);
+ // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the
+ // color. {@link #Paint.setPaint} will clear any previously set alpha value.
+ mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha()));
+ mPaint.setStyle(Style.STROKE);
+ mPaint.setStrokeWidth(mCircleBorderWidth);
+
+ if (mProgressIndeterminate) {
+ mOval.roundOut(mIndeterminateBounds);
+ mIndeterminateDrawable.setBounds(mIndeterminateBounds);
+ mIndeterminateDrawable.setRingColor(mCircleBorderColor);
+ mIndeterminateDrawable.setRingWidth(mCircleBorderWidth);
+ mIndeterminateDrawable.draw(canvas);
+ } else {
+ canvas.drawArc(mOval, -90, 360 * mProgress, false, mPaint);
+ }
+ }
+ if (!mCircleHidden) {
+ mOval.set(paddingLeft, paddingTop, getWidth() - getPaddingRight(),
+ getHeight() - getPaddingBottom());
+ // {@link #Paint.setAlpha} is a helper method that just sets the alpha portion of the
+ // color. {@link #Paint.setPaint} will clear any previously set alpha value.
+ mPaint.setColor(mCurrentColor);
+ mPaint.setAlpha(Math.round(mPaint.getAlpha() * getAlpha()));
+
+ mPaint.setStyle(Style.FILL);
+ float centerX = mOval.centerX();
+ float centerY = mOval.centerY();
+
+ canvas.drawCircle(centerX, centerY, circleRadius, mPaint);
+ }
+
+ if (mDrawable != null) {
+ mDrawable.setAlpha(Math.round(getAlpha() * 255));
+
+ if (mImageTint != null) {
+ mDrawable.setTint(mImageTint);
+ }
+ mDrawable.draw(canvas);
+ }
+
+ super.onDraw(canvas);
+ }
+
+ private void setColorForCurrentState() {
+ int newColor = mCircleColor.getColorForState(getDrawableState(),
+ mCircleColor.getDefaultColor());
+ if (mColorChangeAnimationDurationMs > 0) {
+ if (mColorAnimator != null) {
+ mColorAnimator.cancel();
+ } else {
+ mColorAnimator = new ValueAnimator();
+ }
+ mColorAnimator.setIntValues(new int[] {
+ mCurrentColor, newColor });
+ mColorAnimator.setEvaluator(ARGB_EVALUATOR);
+ mColorAnimator.setDuration(mColorChangeAnimationDurationMs);
+ mColorAnimator.addUpdateListener(this.mAnimationListener);
+ mColorAnimator.start();
+ } else {
+ if (newColor != mCurrentColor) {
+ mCurrentColor = newColor;
+ invalidate();
+ }
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+
+ final float radius = getCircleRadius() + mCircleBorderWidth +
+ mShadowWidth * mShadowVisibility;
+ float desiredWidth = radius * 2;
+ float desiredHeight = radius * 2;
+
+ int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+
+ int width;
+ int height;
+
+ if (widthMode == MeasureSpec.EXACTLY) {
+ width = widthSize;
+ } else if (widthMode == MeasureSpec.AT_MOST) {
+ width = (int) Math.min(desiredWidth, widthSize);
+ } else {
+ width = (int) desiredWidth;
+ }
+
+ if (heightMode == MeasureSpec.EXACTLY) {
+ height = heightSize;
+ } else if (heightMode == MeasureSpec.AT_MOST) {
+ height = (int) Math.min(desiredHeight, heightSize);
+ } else {
+ height = (int) desiredHeight;
+ }
+
+ super.onMeasure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY),
+ MeasureSpec.makeMeasureSpec(height, MeasureSpec.EXACTLY));
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ if (mDrawable != null) {
+ // Retrieve the sizes of the drawable and the view.
+ final int nativeDrawableWidth = mDrawable.getIntrinsicWidth();
+ final int nativeDrawableHeight = mDrawable.getIntrinsicHeight();
+ final int viewWidth = getMeasuredWidth();
+ final int viewHeight = getMeasuredHeight();
+ final float imageCirclePercentage = mImageCirclePercentage > 0
+ ? mImageCirclePercentage : 1;
+
+ final float scaleFactor = Math.min(1f,
+ Math.min(
+ (float) nativeDrawableWidth != 0
+ ? imageCirclePercentage * viewWidth / nativeDrawableWidth : 1,
+ (float) nativeDrawableHeight != 0
+ ? imageCirclePercentage
+ * viewHeight / nativeDrawableHeight : 1));
+
+ // Scale the drawable down to fit the view, if needed.
+ final int drawableWidth = Math.round(scaleFactor * nativeDrawableWidth);
+ final int drawableHeight = Math.round(scaleFactor * nativeDrawableHeight);
+
+ // Center the drawable within the view.
+ final int drawableLeft = (viewWidth - drawableWidth) / 2
+ + Math.round(mImageHorizontalOffcenterPercentage * drawableWidth);
+ final int drawableTop = (viewHeight - drawableHeight) / 2;
+
+ mDrawable.setBounds(drawableLeft, drawableTop, drawableLeft + drawableWidth,
+ drawableTop + drawableHeight);
+ }
+
+ super.onLayout(changed, left, top, right, bottom);
+ }
+
+ public void setImageDrawable(Drawable drawable) {
+ if (drawable != mDrawable) {
+ final Drawable existingDrawable = mDrawable;
+ mDrawable = drawable;
+
+ final boolean skipLayout = drawable != null
+ && existingDrawable != null
+ && existingDrawable.getIntrinsicHeight() == drawable.getIntrinsicHeight()
+ && existingDrawable.getIntrinsicWidth() == drawable.getIntrinsicWidth();
+
+ if (skipLayout) {
+ mDrawable.setBounds(existingDrawable.getBounds());
+ } else {
+ requestLayout();
+ }
+
+ invalidate();
+ }
+ }
+
+ public void setImageResource(int resId) {
+ setImageDrawable(resId == 0 ? null : getContext().getDrawable(resId));
+ }
+
+ public void setImageCirclePercentage(float percentage) {
+ float clamped = Math.max(0, Math.min(1, percentage));
+ if (clamped != mImageCirclePercentage) {
+ mImageCirclePercentage = clamped;
+ invalidate();
+ }
+ }
+
+ public void setImageHorizontalOffcenterPercentage(float percentage) {
+ if (percentage != mImageHorizontalOffcenterPercentage) {
+ mImageHorizontalOffcenterPercentage = percentage;
+ invalidate();
+ }
+ }
+
+ public void setImageTint(int tint) {
+ if (tint != mImageTint) {
+ mImageTint = tint;
+ invalidate();
+ }
+ }
+
+ public float getCircleRadius() {
+ float radius = mCircleRadius;
+ if (mCircleRadius <= 0 && mCircleRadiusPercent > 0) {
+ radius = Math.max(getMeasuredHeight(), getMeasuredWidth()) * mCircleRadiusPercent;
+ }
+
+ return radius - mRadiusInset;
+ }
+
+ public float getCircleRadiusPercent() {
+ return mCircleRadiusPercent;
+ }
+
+ public float getCircleRadiusPressed() {
+ float radius = mCircleRadiusPressed;
+
+ if (mCircleRadiusPressed <= 0 && mCircleRadiusPressedPercent > 0) {
+ radius = Math.max(getMeasuredHeight(), getMeasuredWidth())
+ * mCircleRadiusPressedPercent;
+ }
+
+ return radius - mRadiusInset;
+ }
+
+ public float getCircleRadiusPressedPercent() {
+ return mCircleRadiusPressedPercent;
+ }
+
+ public void setCircleRadius(float circleRadius) {
+ if (circleRadius != mCircleRadius) {
+ mCircleRadius = circleRadius;
+ invalidate();
+ }
+ }
+
+ /**
+ * Sets the radius of the circle to be a percentage of the largest dimension of the view.
+ * @param circleRadiusPercent A {@code float} from 0 to 1 representing the radius percentage.
+ */
+ public void setCircleRadiusPercent(float circleRadiusPercent) {
+ if (circleRadiusPercent != mCircleRadiusPercent) {
+ mCircleRadiusPercent = circleRadiusPercent;
+ invalidate();
+ }
+ }
+
+ public void setCircleRadiusPressed(float circleRadiusPressed) {
+ if (circleRadiusPressed != mCircleRadiusPressed) {
+ mCircleRadiusPressed = circleRadiusPressed;
+ invalidate();
+ }
+ }
+
+ /**
+ * Sets the radius of the circle to be a percentage of the largest dimension of the view when
+ * pressed.
+ * @param circleRadiusPressedPercent A {@code float} from 0 to 1 representing the radius
+ * percentage.
+ */
+ public void setCircleRadiusPressedPercent(float circleRadiusPressedPercent) {
+ if (circleRadiusPressedPercent != mCircleRadiusPressedPercent) {
+ mCircleRadiusPressedPercent = circleRadiusPressedPercent;
+ invalidate();
+ }
+ }
+
+ @Override
+ protected void drawableStateChanged() {
+ super.drawableStateChanged();
+ setColorForCurrentState();
+ }
+
+ public void setCircleColor(int circleColor) {
+ setCircleColorStateList(ColorStateList.valueOf(circleColor));
+ }
+
+ public void setCircleColorStateList(ColorStateList circleColor) {
+ if (!Objects.equals(circleColor, mCircleColor)) {
+ mCircleColor = circleColor;
+ setColorForCurrentState();
+ invalidate();
+ }
+ }
+
+ public ColorStateList getCircleColorStateList() {
+ return mCircleColor;
+ }
+
+ public int getDefaultCircleColor() {
+ return mCircleColor.getDefaultColor();
+ }
+
+ /**
+ * Show the circle border as an indeterminate progress spinner.
+ * The views circle border width and color must be set for this to have an effect.
+ *
+ * @param show true if the progress spinner is shown, false to hide it.
+ */
+ public void showIndeterminateProgress(boolean show) {
+ mProgressIndeterminate = show;
+ if (show) {
+ mIndeterminateDrawable.startAnimation();
+ } else {
+ mIndeterminateDrawable.stopAnimation();
+ }
+ }
+
+ @Override
+ protected void onVisibilityChanged(View changedView, int visibility) {
+ super.onVisibilityChanged(changedView, visibility);
+ if (visibility != View.VISIBLE) {
+ showIndeterminateProgress(false);
+ } else if (mProgressIndeterminate) {
+ showIndeterminateProgress(true);
+ }
+ }
+
+ public void setProgress(float progress) {
+ if (progress != mProgress) {
+ mProgress = progress;
+ invalidate();
+ }
+ }
+
+ /**
+ * Set how much of the shadow should be shown.
+ * @param shadowVisibility Value between 0 and 1.
+ */
+ public void setShadowVisibility(float shadowVisibility) {
+ if (shadowVisibility != mShadowVisibility) {
+ mShadowVisibility = shadowVisibility;
+ invalidate();
+ }
+ }
+
+ public float getInitialCircleRadius() {
+ return mInitialCircleRadius;
+ }
+
+ public void setCircleBorderColor(int circleBorderColor) {
+ mCircleBorderColor = circleBorderColor;
+ }
+
+ /**
+ * Set the border around the circle.
+ * @param circleBorderWidth Width of the border around the circle.
+ */
+ public void setCircleBorderWidth(float circleBorderWidth) {
+ if (circleBorderWidth != mCircleBorderWidth) {
+ mCircleBorderWidth = circleBorderWidth;
+ invalidate();
+ }
+ }
+
+ @Override
+ public void setPressed(boolean pressed) {
+ super.setPressed(pressed);
+ if (pressed != mPressed) {
+ mPressed = pressed;
+ invalidate();
+ }
+ }
+
+ public Drawable getImageDrawable() {
+ return mDrawable;
+ }
+
+ /**
+ * @return the milliseconds duration of the transition animation when the color changes.
+ */
+ public long getColorChangeAnimationDuration() {
+ return mColorChangeAnimationDurationMs;
+ }
+
+ /**
+ * @param mColorChangeAnimationDurationMs the milliseconds duration of the color change
+ * animation. The color change animation will run if the color changes with {@link #setCircleColor}
+ * or as a result of the active state changing.
+ */
+ public void setColorChangeAnimationDuration(long mColorChangeAnimationDurationMs) {
+ this.mColorChangeAnimationDurationMs = mColorChangeAnimationDurationMs;
+ }
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/Gusterpolator.java b/packages/PackageInstaller/src/android/support/wearable/view/Gusterpolator.java
new file mode 100644
index 0000000000000..6b17100fe9a87
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/Gusterpolator.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 androidx.wear.ble.view;
+
+import android.animation.TimeInterpolator;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+/**
+ * Interpolator that uses a Bezier derived S shaped curve.
+ * @hide
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+class Gusterpolator implements TimeInterpolator {
+
+ /** An instance of {@link androidx.wear.ble.view.Gusterpolator}. */
+ public static final Gusterpolator INSTANCE = new Gusterpolator();
+
+ /**
+ * To avoid users of this class creating multiple copies needlessly, the constructor is
+ * private.
+ */
+ private Gusterpolator() {}
+
+ /**
+ * Lookup table values.
+ * Generated using a Bezier curve from (0,0) to (1,1) with control points:
+ * P0 (0,0)
+ * P1 (0.4, 0)
+ * P2 (0.2, 1.0)
+ * P3 (1.0, 1.0)
+ *
+ * Values sampled with x at regular intervals between 0 and 1.
+ */
+ private static final float[] VALUES = new float[] {
+ 0.0f, 0.0002f, 0.0009f, 0.0019f, 0.0036f, 0.0059f, 0.0086f, 0.0119f, 0.0157f, 0.0209f,
+ 0.0257f, 0.0321f, 0.0392f, 0.0469f, 0.0566f, 0.0656f, 0.0768f, 0.0887f, 0.1033f, 0.1186f,
+ 0.1349f, 0.1519f, 0.1696f, 0.1928f, 0.2121f, 0.237f, 0.2627f, 0.2892f, 0.3109f, 0.3386f,
+ 0.3667f, 0.3952f, 0.4241f, 0.4474f, 0.4766f, 0.5f, 0.5234f, 0.5468f, 0.5701f, 0.5933f,
+ 0.6134f, 0.6333f, 0.6531f, 0.6698f, 0.6891f, 0.7054f, 0.7214f, 0.7346f, 0.7502f, 0.763f,
+ 0.7756f, 0.7879f, 0.8f, 0.8107f, 0.8212f, 0.8326f, 0.8415f, 0.8503f, 0.8588f, 0.8672f,
+ 0.8754f, 0.8833f, 0.8911f, 0.8977f, 0.9041f, 0.9113f, 0.9165f, 0.9232f, 0.9281f, 0.9328f,
+ 0.9382f, 0.9434f, 0.9476f, 0.9518f, 0.9557f, 0.9596f, 0.9632f, 0.9662f, 0.9695f, 0.9722f,
+ 0.9753f, 0.9777f, 0.9805f, 0.9826f, 0.9847f, 0.9866f, 0.9884f, 0.9901f, 0.9917f, 0.9931f,
+ 0.9944f, 0.9955f, 0.9964f, 0.9973f, 0.9981f, 0.9986f, 0.9992f, 0.9995f, 0.9998f, 1.0f, 1.0f
+ };
+
+ private static final float STEP_SIZE = 1.0f / (VALUES.length - 1);
+
+ @Override
+ public float getInterpolation(float input) {
+ if (input >= 1.0f) {
+ return 1.0f;
+ }
+
+ if (input <= 0f) {
+ return 0f;
+ }
+
+ int position = Math.min(
+ (int)(input * (VALUES.length - 1)),
+ VALUES.length - 2);
+
+ float quantized = position * STEP_SIZE;
+ float difference = input - quantized;
+ float weight = difference / STEP_SIZE;
+
+ return VALUES[position] + weight * (VALUES[position + 1] - VALUES[position]);
+ }
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/ProgressDrawable.java b/packages/PackageInstaller/src/android/support/wearable/view/ProgressDrawable.java
new file mode 100644
index 0000000000000..51ec8f07f1030
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/ProgressDrawable.java
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 androidx.wear.ble.view;
+
+import android.animation.ObjectAnimator;
+import android.animation.TimeInterpolator;
+import android.animation.ValueAnimator;
+import android.annotation.TargetApi;
+import android.graphics.Canvas;
+import android.graphics.ColorFilter;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.RectF;
+import android.graphics.drawable.Drawable;
+import android.os.Build;
+import android.util.Property;
+import android.view.animation.LinearInterpolator;
+
+/**
+ * Drawable for showing an indeterminate progress indicator.
+ *
+ * TODO: When Material progress drawable is available in the support library stop using this.
+ *
+ * @hide
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+class ProgressDrawable extends Drawable {
+
+ private static Property LEVEL =
+ new Property(Integer.class, "level") {
+ @Override
+ public Integer get(ProgressDrawable drawable) {
+ return drawable.getLevel();
+ }
+
+ @Override
+ public void set(ProgressDrawable drawable, Integer value) {
+ drawable.setLevel(value);
+ drawable.invalidateSelf();
+ }
+ };
+ /** Max level for a level drawable, as specified in developer docs for {@link Drawable}. */
+ private static final int MAX_LEVEL = 10000;
+
+ /** How many different sections are there, five gives us the material style star. **/
+ private static final int NUMBER_OF_SEGMENTS = 5;
+
+ private static final int LEVELS_PER_SEGMENT = MAX_LEVEL / NUMBER_OF_SEGMENTS;
+ private static final float STARTING_ANGLE = -90f;
+ private static final long ANIMATION_DURATION = 6000;
+ private static final int FULL_CIRCLE = 360;
+ private static final int MAX_SWEEP = 306;
+ private static final int CORRECTION_ANGLE = FULL_CIRCLE - MAX_SWEEP;
+ /** How far through each cycle does the bar stop growing and start shrinking, half way. **/
+ private static final float GROW_SHRINK_RATIO = 0.5f;
+ // TODO: replace this with BakedBezierInterpolator when its available in support library.
+ private static final TimeInterpolator mInterpolator = Gusterpolator.INSTANCE;
+
+ private final RectF mInnerCircleBounds = new RectF();
+ private final Paint mPaint = new Paint();
+ private final ObjectAnimator mAnimator;
+ private float mCircleBorderWidth;
+ private int mCircleBorderColor;
+
+ public ProgressDrawable() {
+ mPaint.setAntiAlias(true);
+ mPaint.setStyle(Paint.Style.STROKE);
+ mAnimator = ObjectAnimator.ofInt(this, LEVEL, 0, MAX_LEVEL);
+ mAnimator.setRepeatCount(ValueAnimator.INFINITE);
+ mAnimator.setRepeatMode(ValueAnimator.RESTART);
+ mAnimator.setDuration(ANIMATION_DURATION);
+ mAnimator.setInterpolator(new LinearInterpolator());
+ }
+
+ public void setRingColor(int color) {
+ mCircleBorderColor = color;
+ }
+
+ public void setRingWidth(float width) {
+ mCircleBorderWidth = width;
+ }
+
+ public void startAnimation() {
+ mAnimator.start();
+ }
+
+ public void stopAnimation() {
+ mAnimator.cancel();
+ }
+
+ @Override
+ public void draw(Canvas canvas) {
+ canvas.save();
+ mInnerCircleBounds.set(getBounds());
+ mInnerCircleBounds.inset(mCircleBorderWidth / 2.0f, mCircleBorderWidth / 2.0f);
+ mPaint.setStrokeWidth(mCircleBorderWidth);
+ mPaint.setColor(mCircleBorderColor);
+
+ float sweepAngle = FULL_CIRCLE;
+ boolean growing = false;
+ float correctionAngle = 0;
+ int level = getLevel();
+
+ int currentSegment = level / LEVELS_PER_SEGMENT;
+ int offset = currentSegment * LEVELS_PER_SEGMENT;
+ float progress = (level - offset) / (float) LEVELS_PER_SEGMENT;
+
+ growing = progress < GROW_SHRINK_RATIO;
+ correctionAngle = CORRECTION_ANGLE * progress;
+
+ if (growing) {
+ sweepAngle = MAX_SWEEP * mInterpolator.getInterpolation(
+ lerpInv(0f, GROW_SHRINK_RATIO, progress));
+ } else {
+ sweepAngle = MAX_SWEEP * (1.0f - mInterpolator.getInterpolation(
+ lerpInv(GROW_SHRINK_RATIO, 1.0f, progress)));
+ }
+
+ sweepAngle = Math.max(1, sweepAngle);
+
+ canvas.rotate(
+ level * (1.0f / MAX_LEVEL) * 2 * FULL_CIRCLE + STARTING_ANGLE + correctionAngle,
+ mInnerCircleBounds.centerX(),
+ mInnerCircleBounds.centerY());
+ canvas.drawArc(mInnerCircleBounds,
+ growing ? 0 : MAX_SWEEP - sweepAngle,
+ sweepAngle,
+ false,
+ mPaint);
+ canvas.restore();
+ }
+
+ @Override
+ public void setAlpha(int i) {
+ // Not supported.
+ }
+
+ @Override
+ public void setColorFilter(ColorFilter colorFilter) {
+ // Not supported.
+ }
+
+ @Override
+ public int getOpacity() {
+ return PixelFormat.OPAQUE;
+ }
+
+ @Override
+ protected boolean onLevelChange(int level) {
+ return true; // Changing the level of this drawable does change its appearance.
+ }
+
+ /**
+ * Returns the interpolation scalar (s) that satisfies the equation:
+ * {@code value = }lerp(a, b, s)
+ *
+ *
If {@code a == b}, then this function will return 0.
+ */
+ private static float lerpInv(float a, float b, float value) {
+ return a != b ? ((value - a) / (b - a)) : 0.0f;
+ }
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/SimpleAnimatorListener.java b/packages/PackageInstaller/src/android/support/wearable/view/SimpleAnimatorListener.java
new file mode 100644
index 0000000000000..c8fe58c43f4ed
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/SimpleAnimatorListener.java
@@ -0,0 +1,67 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 androidx.wear.ble.view;
+
+import android.animation.Animator;
+import android.annotation.TargetApi;
+import android.os.Build;
+
+/**
+ * Convenience class for listening for Animator events that implements the AnimatorListener
+ * interface and allows extending only methods that are necessary.
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+public class SimpleAnimatorListener implements Animator.AnimatorListener {
+
+ private boolean mWasCanceled;
+
+ @Override
+ public void onAnimationCancel(Animator animator) {
+ mWasCanceled = true;
+ }
+
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (!mWasCanceled) {
+ onAnimationComplete(animator);
+ }
+ }
+
+ @Override
+ public void onAnimationRepeat(Animator animator) {
+ }
+
+ @Override
+ public void onAnimationStart(Animator animator) {
+ mWasCanceled = false;
+ }
+
+ /**
+ * Called when the animation finishes. Not called if the animation was canceled.
+ */
+ public void onAnimationComplete(Animator animator) {
+ }
+
+ /**
+ * Provides information if the animation was cancelled.
+ * @return True if animation was cancelled.
+ */
+ public boolean wasCanceled() {
+ return mWasCanceled;
+ }
+
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/WearableDialogHelper.java b/packages/PackageInstaller/src/android/support/wearable/view/WearableDialogHelper.java
new file mode 100644
index 0000000000000..0151ed573b04b
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/WearableDialogHelper.java
@@ -0,0 +1,217 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package androidx.wear.ble.view;
+
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import androidx.annotation.DrawableRes;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import androidx.annotation.VisibleForTesting;
+import android.util.Log;
+import android.widget.Button;
+
+/**
+ * Helper to add icons to AlertDialog buttons.AlertDialog buttons.
+ */
+public class WearableDialogHelper {
+ private static final String TAG = "WearableDialogHelper";
+
+ private int mPositiveIconId;
+ private Drawable mPositiveIcon;
+
+ private int mNeutralIconId;
+ private Drawable mNeutralIcon;
+
+ private int mNegativeIconId;
+ private Drawable mNegativeIcon;
+
+ @VisibleForTesting /* package */ Resources mResources;
+ @VisibleForTesting /* package */ Resources.Theme mTheme;
+
+ /**
+ * Convenience constructor, equivalent to {@code new WearableDialogHelper(context.getResources(),
+ * context.getTheme())}.
+ */
+ public WearableDialogHelper(@NonNull Context context) {
+ this(context.getResources(), context.getTheme());
+ }
+
+ /**
+ * @param resources the Resources used to obtain Drawables from resource IDs.
+ * @param theme the Theme used to properly obtain Drawables from resource IDs.
+ */
+ public WearableDialogHelper(@NonNull Resources resources, @NonNull Resources.Theme theme) {
+ mResources = resources;
+ mTheme = theme;
+ }
+
+ @Nullable
+ public Drawable getPositiveIcon() {
+ return resolveDrawable(mPositiveIcon, mPositiveIconId);
+ }
+
+ @Nullable
+ public Drawable getNegativeIcon() {
+ return resolveDrawable(mNegativeIcon, mNegativeIconId);
+ }
+
+ @Nullable
+ public Drawable getNeutralIcon() {
+ return resolveDrawable(mNeutralIcon, mNeutralIconId);
+ }
+
+ @NonNull
+ public WearableDialogHelper setPositiveIcon(@DrawableRes int resId) {
+ mPositiveIconId = resId;
+ mPositiveIcon = null;
+ return this;
+ }
+
+ @NonNull
+ public WearableDialogHelper setPositiveIcon(@Nullable Drawable icon) {
+ mPositiveIcon = icon;
+ mPositiveIconId = 0;
+ return this;
+ }
+
+ @NonNull
+ public WearableDialogHelper setNegativeIcon(@DrawableRes int resId) {
+ mNegativeIconId = resId;
+ mNegativeIcon = null;
+ return this;
+ }
+
+ @NonNull
+ public WearableDialogHelper setNegativeIcon(@Nullable Drawable icon) {
+ mNegativeIcon = icon;
+ mNegativeIconId = 0;
+ return this;
+ }
+
+ @NonNull
+ public WearableDialogHelper setNeutralIcon(@DrawableRes int resId) {
+ mNeutralIconId = resId;
+ mNeutralIcon = null;
+ return this;
+ }
+
+ @NonNull
+ public WearableDialogHelper setNeutralIcon(@Nullable Drawable icon) {
+ mNeutralIcon = icon;
+ mNeutralIconId = 0;
+ return this;
+ }
+
+ /**
+ * Applies the button icons setup in the helper to the buttons in the dialog.
+ *
+ *
Note that this should be called after {@code AlertDialog.create()}, NOT {@code
+ * AlertDialog.Builder.create()}. Calling {@code AlertDialog.Builder.show()} would also accomplish
+ * the same thing.
+ *
+ * @param dialog the AlertDialog to style with the helper.
+ */
+ public void apply(@NonNull AlertDialog dialog) {
+ applyButton(dialog.getButton(DialogInterface.BUTTON_POSITIVE), getPositiveIcon());
+ applyButton(dialog.getButton(DialogInterface.BUTTON_NEGATIVE), getNegativeIcon());
+ applyButton(dialog.getButton(DialogInterface.BUTTON_NEUTRAL), getNeutralIcon());
+ }
+
+ /** Applies the specified drawable to the button. */
+ @VisibleForTesting
+ /* package */ void applyButton(@Nullable Button button, @Nullable Drawable drawable) {
+ if (button != null) {
+ button.setCompoundDrawablesRelativeWithIntrinsicBounds(drawable, null, null, null);
+ button.setAllCaps(false);
+ } else if (drawable != null) {
+ Log.w(TAG, "non-null drawable used with missing button, did you call AlertDialog.create()?");
+ }
+ }
+
+ /** Obtain a drawable between a drawable and a resource ID. */
+ @VisibleForTesting
+ /* package */ Drawable resolveDrawable(@Nullable Drawable drawable, @DrawableRes int resId) {
+ return drawable == null && resId != 0 ? mResources.getDrawable(resId, mTheme) : drawable;
+ }
+
+ /** Convenience builder to generate an AlertDialog with icons in buttons. */
+ public static class DialogBuilder extends AlertDialog.Builder {
+ private final WearableDialogHelper mHelper;
+
+ public DialogBuilder(Context context) {
+ super(context);
+ mHelper = new WearableDialogHelper(context.getResources(), context.getTheme());
+ }
+
+ public DialogBuilder(Context context, int themeResId) {
+ super(context, themeResId);
+ mHelper = new WearableDialogHelper(context.getResources(), context.getTheme());
+ }
+
+ public WearableDialogHelper getHelper() {
+ return mHelper;
+ }
+
+ public DialogBuilder setPositiveIcon(@DrawableRes int iconId) {
+ mHelper.setPositiveIcon(iconId);
+ return this;
+ }
+
+ public DialogBuilder setPositiveIcon(@Nullable Drawable icon) {
+ mHelper.setPositiveIcon(icon);
+ return this;
+ }
+
+ public DialogBuilder setNegativeIcon(@DrawableRes int iconId) {
+ mHelper.setNegativeIcon(iconId);
+ return this;
+ }
+
+ public DialogBuilder setNegativeIcon(@Nullable Drawable icon) {
+ mHelper.setNegativeIcon(icon);
+ return this;
+ }
+
+ public DialogBuilder setNeutralIcon(@DrawableRes int iconId) {
+ mHelper.setNeutralIcon(iconId);
+ return this;
+ }
+
+ public DialogBuilder setNeutralIcon(@Nullable Drawable icon) {
+ mHelper.setNeutralIcon(icon);
+ return this;
+ }
+
+ @Override
+ public AlertDialog create() {
+ final AlertDialog dialog = super.create();
+ dialog.create();
+ mHelper.apply(dialog);
+ return dialog;
+ }
+
+ @Override
+ public AlertDialog show() {
+ final AlertDialog dialog = this.create();
+ dialog.show();
+ return dialog;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/android/support/wearable/view/WearableListView.java b/packages/PackageInstaller/src/android/support/wearable/view/WearableListView.java
new file mode 100644
index 0000000000000..956ff9e9e6312
--- /dev/null
+++ b/packages/PackageInstaller/src/android/support/wearable/view/WearableListView.java
@@ -0,0 +1,1387 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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 androidx.wear.ble.view;
+
+import android.animation.Animator;
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.graphics.PointF;
+import android.os.Build;
+import android.os.Handler;
+import androidx.recyclerview.widget.LinearSmoothScroller;
+import androidx.recyclerview.widget.RecyclerView;
+import android.util.AttributeSet;
+import android.util.DisplayMetrics;
+import android.util.Log;
+import android.util.Property;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.ViewGroup;
+import android.widget.Scroller;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * An alternative version of ListView that is optimized for ease of use on small screen wearable
+ * devices. It displays a vertically scrollable list of items, and automatically snaps to the
+ * nearest item when the user stops scrolling.
+ *
+ *
+ * For a quick start, you will need to implement a subclass of {@link .Adapter},
+ * which will create and bind your views to the {@link .ViewHolder} objects. If you want to add
+ * more visual treatment to your views when they become the central items of the
+ * WearableListView, have them implement the {@link .OnCenterProximityListener} interface.
+ *
+ */
+@TargetApi(Build.VERSION_CODES.KITKAT_WATCH)
+public class WearableListView extends RecyclerView {
+ @SuppressWarnings("unused")
+ private static final String TAG = "WearableListView";
+
+ private static final long FLIP_ANIMATION_DURATION_MS = 150;
+ private static final long CENTERING_ANIMATION_DURATION_MS = 150;
+
+ private static final float TOP_TAP_REGION_PERCENTAGE = .33f;
+ private static final float BOTTOM_TAP_REGION_PERCENTAGE = .33f;
+
+ // Each item will occupy one third of the height.
+ private static final int THIRD = 3;
+
+ private final int mMinFlingVelocity;
+ private final int mMaxFlingVelocity;
+
+ private boolean mMaximizeSingleItem;
+ private boolean mCanClick = true;
+ // WristGesture navigation signals are delivered as KeyEvents. Allow developer to disable them
+ // for this specific View. It might be cleaner to simply have users re-implement onKeyDown().
+ // TOOD: Finalize the disabling mechanism here.
+ private boolean mGestureNavigationEnabled = true;
+ private int mTapPositionX;
+ private int mTapPositionY;
+ private ClickListener mClickListener;
+
+ private Animator mScrollAnimator;
+ // This is a little hacky due to the fact that animator provides incremental values instead of
+ // deltas and scrolling code requires deltas. We animate WearableListView directly and use this
+ // field to calculate deltas. Obviously this means that only one scrolling algorithm can run at
+ // a time, but I don't think it would be wise to have more than one running.
+ private int mLastScrollChange;
+
+ private SetScrollVerticallyProperty mSetScrollVerticallyProperty =
+ new SetScrollVerticallyProperty();
+
+ private final List mOnScrollListeners = new ArrayList();
+
+ private final List mOnCentralPositionChangedListeners =
+ new ArrayList();
+
+ private OnOverScrollListener mOverScrollListener;
+
+ private boolean mGreedyTouchMode;
+
+ private float mStartX;
+
+ private float mStartY;
+
+ private float mStartFirstTop;
+
+ private final int mTouchSlop;
+
+ private boolean mPossibleVerticalSwipe;
+
+ private int mInitialOffset = 0;
+
+ private Scroller mScroller;
+
+ // Top and bottom boundaries for tap checking. Need to recompute by calling computeTapRegions
+ // before referencing.
+ private final float[] mTapRegions = new float[2];
+
+ private boolean mGestureDirectionLocked;
+ private int mPreviousCentral = 0;
+
+ // Temp variable for storing locations on screen.
+ private final int[] mLocation = new int[2];
+
+ // TODO: Consider clearing this when underlying data set changes. If the data set changes, you
+ // can't safely assume that this pressed view is in the same place as it was before and it will
+ // receive setPressed(false) unnecessarily. In theory it should be fine, but in practice we
+ // have places like this: mIconView.setCircleColor(pressed ? mPressedColor : mSelectedColor);
+ // This might set selected color on non selected item. Our logic should be: if you change
+ // underlying data set, all best are off and you need to preserve the state; we will clear
+ // this field. However, I am not willing to introduce this so late in C development.
+ private View mPressedView = null;
+
+ private final Runnable mPressedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ if (getChildCount() > 0) {
+ mPressedView = getChildAt(findCenterViewIndex());
+ mPressedView.setPressed(true);
+ } else {
+ Log.w(TAG, "mPressedRunnable: the children were removed, skipping.");
+ }
+ }
+ };
+
+ private final Runnable mReleasedRunnable = new Runnable() {
+ @Override
+ public void run() {
+ releasePressedItem();
+ }
+ };
+
+ private Runnable mNotifyChildrenPostLayoutRunnable = new Runnable() {
+ @Override
+ public void run() {
+ notifyChildrenAboutProximity(false);
+ }
+ };
+
+ private final AdapterDataObserver mObserver = new AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ WearableListView.this.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+ @Override
+ public void onLayoutChange(View v, int left, int top, int right, int bottom,
+ int oldLeft, int oldTop, int oldRight, int oldBottom) {
+ WearableListView.this.removeOnLayoutChangeListener(this);
+ if (WearableListView.this.getChildCount() > 0) {
+ WearableListView.this.animateToCenter();
+ }
+ }
+ });
+ }
+ };
+
+ public WearableListView(Context context) {
+ this(context, null);
+ }
+
+ public WearableListView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public WearableListView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ setHasFixedSize(true);
+ setOverScrollMode(View.OVER_SCROLL_NEVER);
+ setLayoutManager(new LayoutManager());
+
+ final RecyclerView.OnScrollListener onScrollListener = new RecyclerView.OnScrollListener() {
+ @Override
+ public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
+ if (newState == RecyclerView.SCROLL_STATE_IDLE && getChildCount() > 0) {
+ handleTouchUp(null, newState);
+ }
+ for (OnScrollListener listener : mOnScrollListeners) {
+ listener.onScrollStateChanged(newState);
+ }
+ }
+
+ @Override
+ public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
+ onScroll(dy);
+ }
+ };
+ setOnScrollListener(onScrollListener);
+
+ final ViewConfiguration vc = ViewConfiguration.get(context);
+ mTouchSlop = vc.getScaledTouchSlop();
+
+ mMinFlingVelocity = vc.getScaledMinimumFlingVelocity();
+ mMaxFlingVelocity = vc.getScaledMaximumFlingVelocity();
+ }
+
+ @Override
+ public void setAdapter(RecyclerView.Adapter adapter) {
+ RecyclerView.Adapter currentAdapter = getAdapter();
+ if (currentAdapter != null) {
+ currentAdapter.unregisterAdapterDataObserver(mObserver);
+ }
+
+ super.setAdapter(adapter);
+
+ if (adapter != null) {
+ adapter.registerAdapterDataObserver(mObserver);
+ }
+ }
+
+ /**
+ * @return the position of the center child's baseline; -1 if no center child exists or if
+ * the center child does not return a valid baseline.
+ */
+ @Override
+ public int getBaseline() {
+ // No children implies there is no center child for which a baseline can be computed.
+ if (getChildCount() == 0) {
+ return super.getBaseline();
+ }
+
+ // Compute the baseline of the center child.
+ final int centerChildIndex = findCenterViewIndex();
+ final int centerChildBaseline = getChildAt(centerChildIndex).getBaseline();
+
+ // If the center child has no baseline, neither does this list view.
+ if (centerChildBaseline == -1) {
+ return super.getBaseline();
+ }
+
+ return getCentralViewTop() + centerChildBaseline;
+ }
+
+ /**
+ * @return true if the list is scrolled all the way to the top.
+ */
+ public boolean isAtTop() {
+ if (getChildCount() == 0) {
+ return true;
+ }
+
+ int centerChildIndex = findCenterViewIndex();
+ View centerView = getChildAt(centerChildIndex);
+ return getChildAdapterPosition(centerView) == 0 &&
+ getScrollState() == RecyclerView.SCROLL_STATE_IDLE;
+ }
+
+ /**
+ * Clears the state of the layout manager that positions list items.
+ */
+ public void resetLayoutManager() {
+ setLayoutManager(new LayoutManager());
+ }
+
+ /**
+ * Controls whether WearableListView should intercept all touch events and also prevent the
+ * parent from receiving them.
+ * @param greedy If true it will intercept all touch events.
+ */
+ public void setGreedyTouchMode(boolean greedy) {
+ mGreedyTouchMode = greedy;
+ }
+
+ /**
+ * By default the first element of the list is initially positioned in the center of the screen.
+ * This method allows the developer to specify a different offset, e.g. to hide the
+ * WearableListView before the user is allowed to use it.
+ *
+ * @param top How far the elements should be pushed down.
+ */
+ public void setInitialOffset(int top) {
+ mInitialOffset = top;
+ }
+
+ @Override
+ public boolean onInterceptTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ if (mGreedyTouchMode && getChildCount() > 0) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ mStartX = event.getX();
+ mStartY = event.getY();
+ mStartFirstTop = getChildCount() > 0 ? getChildAt(0).getTop() : 0;
+ mPossibleVerticalSwipe = true;
+ mGestureDirectionLocked = false;
+ } else if (action == MotionEvent.ACTION_MOVE && mPossibleVerticalSwipe) {
+ handlePossibleVerticalSwipe(event);
+ }
+ getParent().requestDisallowInterceptTouchEvent(mPossibleVerticalSwipe);
+ }
+ return super.onInterceptTouchEvent(event);
+ }
+
+ private boolean handlePossibleVerticalSwipe(MotionEvent event) {
+ if (mGestureDirectionLocked) {
+ return mPossibleVerticalSwipe;
+ }
+ float deltaX = Math.abs(mStartX - event.getX());
+ float deltaY = Math.abs(mStartY - event.getY());
+ float distance = (deltaX * deltaX) + (deltaY * deltaY);
+ // Verify that the distance moved in the combined x/y direction is at
+ // least touch slop before determining the gesture direction.
+ if (distance > (mTouchSlop * mTouchSlop)) {
+ if (deltaX > deltaY) {
+ mPossibleVerticalSwipe = false;
+ }
+ mGestureDirectionLocked = true;
+ }
+ return mPossibleVerticalSwipe;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ if (!isEnabled()) {
+ return false;
+ }
+
+ // super.onTouchEvent can change the state of the scroll, keep a copy so that handleTouchUp
+ // can exit early if scrollState != IDLE when the touch event started.
+ int scrollState = getScrollState();
+ boolean result = super.onTouchEvent(event);
+ if (getChildCount() > 0) {
+ int action = event.getActionMasked();
+ if (action == MotionEvent.ACTION_DOWN) {
+ handleTouchDown(event);
+ } else if (action == MotionEvent.ACTION_UP) {
+ handleTouchUp(event, scrollState);
+ getParent().requestDisallowInterceptTouchEvent(false);
+ } else if (action == MotionEvent.ACTION_MOVE) {
+ if (Math.abs(mTapPositionX - (int) event.getX()) >= mTouchSlop ||
+ Math.abs(mTapPositionY - (int) event.getY()) >= mTouchSlop) {
+ releasePressedItem();
+ mCanClick = false;
+ }
+ result |= handlePossibleVerticalSwipe(event);
+ getParent().requestDisallowInterceptTouchEvent(mPossibleVerticalSwipe);
+ } else if (action == MotionEvent.ACTION_CANCEL) {
+ getParent().requestDisallowInterceptTouchEvent(false);
+ mCanClick = true;
+ }
+ }
+ return result;
+ }
+
+ private void releasePressedItem() {
+ if (mPressedView != null) {
+ mPressedView.setPressed(false);
+ mPressedView = null;
+ }
+ Handler handler = getHandler();
+ if (handler != null) {
+ handler.removeCallbacks(mPressedRunnable);
+ }
+ }
+
+ private void onScroll(int dy) {
+ for (OnScrollListener listener : mOnScrollListeners) {
+ listener.onScroll(dy);
+ }
+ notifyChildrenAboutProximity(true);
+ }
+
+ /**
+ * Adds a listener that will be called when the content of the list view is scrolled.
+ */
+ public void addOnScrollListener(OnScrollListener listener) {
+ mOnScrollListeners.add(listener);
+ }
+
+ /**
+ * Removes listener for scroll events.
+ */
+ public void removeOnScrollListener(OnScrollListener listener) {
+ mOnScrollListeners.remove(listener);
+ }
+
+ /**
+ * Adds a listener that will be called when the central item of the list changes.
+ */
+ public void addOnCentralPositionChangedListener(OnCentralPositionChangedListener listener) {
+ mOnCentralPositionChangedListeners.add(listener);
+ }
+
+ /**
+ * Removes a listener that would be called when the central item of the list changes.
+ */
+ public void removeOnCentralPositionChangedListener(OnCentralPositionChangedListener listener) {
+ mOnCentralPositionChangedListeners.remove(listener);
+ }
+
+ /**
+ * Determines if navigation of list with wrist gestures is enabled.
+ */
+ public boolean isGestureNavigationEnabled() {
+ return mGestureNavigationEnabled;
+ }
+
+ /**
+ * Sets whether navigation of list with wrist gestures is enabled.
+ */
+ public void setEnableGestureNavigation(boolean enabled) {
+ mGestureNavigationEnabled = enabled;
+ }
+
+ @Override /* KeyEvent.Callback */
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // Respond to keycodes (at least originally generated and injected by wrist gestures).
+ if (mGestureNavigationEnabled) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_NAVIGATE_PREVIOUS:
+ fling(0, -mMinFlingVelocity);
+ return true;
+ case KeyEvent.KEYCODE_NAVIGATE_NEXT:
+ fling(0, mMinFlingVelocity);
+ return true;
+ case KeyEvent.KEYCODE_NAVIGATE_IN:
+ return tapCenterView();
+ case KeyEvent.KEYCODE_NAVIGATE_OUT:
+ // Returing false leaves the action to the container of this WearableListView
+ // (e.g. finishing the activity containing this WearableListView).
+ return false;
+ }
+ }
+ return super.onKeyDown(keyCode, event);
+ }
+
+ /**
+ * Simulate tapping the child view at the center of this list.
+ */
+ private boolean tapCenterView() {
+ if (!isEnabled() || getVisibility() != View.VISIBLE) {
+ return false;
+ }
+ int index = findCenterViewIndex();
+ View view = getChildAt(index);
+ ViewHolder holder = getChildViewHolder(view);
+ if (mClickListener != null) {
+ mClickListener.onClick(holder);
+ return true;
+ }
+ return false;
+ }
+
+ private boolean checkForTap(MotionEvent event) {
+ // No taps are accepted if this view is disabled.
+ if (!isEnabled()) {
+ return false;
+ }
+
+ float rawY = event.getRawY();
+ int index = findCenterViewIndex();
+ View view = getChildAt(index);
+ ViewHolder holder = getChildViewHolder(view);
+ computeTapRegions(mTapRegions);
+ if (rawY > mTapRegions[0] && rawY < mTapRegions[1]) {
+ if (mClickListener != null) {
+ mClickListener.onClick(holder);
+ }
+ return true;
+ }
+ if (index > 0 && rawY <= mTapRegions[0]) {
+ animateToMiddle(index - 1, index);
+ return true;
+ }
+ if (index < getChildCount() - 1 && rawY >= mTapRegions[1]) {
+ animateToMiddle(index + 1, index);
+ return true;
+ }
+ if (index == 0 && rawY <= mTapRegions[0] && mClickListener != null) {
+ // Special case: if the top third of the screen is empty and the touch event happens
+ // there, we don't want to immediately disallow the parent from using it. We tell
+ // parent to disallow intercept only after we locked a gesture. Before that he
+ // might do something with the action.
+ mClickListener.onTopEmptyRegionClick();
+ return true;
+ }
+ return false;
+ }
+
+ private void animateToMiddle(int newCenterIndex, int oldCenterIndex) {
+ if (newCenterIndex == oldCenterIndex) {
+ throw new IllegalArgumentException(
+ "newCenterIndex must be different from oldCenterIndex");
+ }
+ List animators = new ArrayList();
+ View child = getChildAt(newCenterIndex);
+ int scrollToMiddle = getCentralViewTop() - child.getTop();
+ startScrollAnimation(animators, scrollToMiddle, FLIP_ANIMATION_DURATION_MS);
+ }
+
+ private void startScrollAnimation(List animators, int scroll, long duration) {
+ startScrollAnimation(animators, scroll, duration, 0);
+ }
+
+ private void startScrollAnimation(List animators, int scroll, long duration,
+ long delay) {
+ startScrollAnimation(animators, scroll, duration, delay, null);
+ }
+
+ private void startScrollAnimation(
+ int scroll, long duration, long delay, Animator.AnimatorListener listener) {
+ startScrollAnimation(null, scroll, duration, delay, listener);
+ }
+
+ private void startScrollAnimation(List animators, int scroll, long duration,
+ long delay, Animator.AnimatorListener listener) {
+ if (mScrollAnimator != null) {
+ mScrollAnimator.cancel();
+ }
+
+ mLastScrollChange = 0;
+ ObjectAnimator scrollAnimator = ObjectAnimator.ofInt(this, mSetScrollVerticallyProperty,
+ 0, -scroll);
+
+ if (animators != null) {
+ animators.add(scrollAnimator);
+ AnimatorSet animatorSet = new AnimatorSet();
+ animatorSet.playTogether(animators);
+ mScrollAnimator = animatorSet;
+ } else {
+ mScrollAnimator = scrollAnimator;
+ }
+ mScrollAnimator.setDuration(duration);
+ if (listener != null) {
+ mScrollAnimator.addListener(listener);
+ }
+ if (delay > 0) {
+ mScrollAnimator.setStartDelay(delay);
+ }
+ mScrollAnimator.start();
+ }
+
+ @Override
+ public boolean fling(int velocityX, int velocityY) {
+ if (getChildCount() == 0) {
+ return false;
+ }
+ // If we are flinging towards empty space (before first element or after last), we reuse
+ // original flinging mechanism.
+ final int index = findCenterViewIndex();
+ final View child = getChildAt(index);
+ int currentPosition = getChildPosition(child);
+ if ((currentPosition == 0 && velocityY < 0) ||
+ (currentPosition == getAdapter().getItemCount() - 1 && velocityY > 0)) {
+ return super.fling(velocityX, velocityY);
+ }
+
+ if (Math.abs(velocityY) < mMinFlingVelocity) {
+ return false;
+ }
+ velocityY = Math.max(Math.min(velocityY, mMaxFlingVelocity), -mMaxFlingVelocity);
+
+ if (mScroller == null) {
+ mScroller = new Scroller(getContext(), null, true);
+ }
+ mScroller.fling(0, 0, 0, velocityY, Integer.MIN_VALUE, Integer.MAX_VALUE,
+ Integer.MIN_VALUE, Integer.MAX_VALUE);
+ int finalY = mScroller.getFinalY();
+ int delta = finalY / (getPaddingTop() + getAdjustedHeight() / 2);
+ if (delta == 0) {
+ // If the fling would not be enough to change position, we increase it to satisfy user's
+ // intent of switching current position.
+ delta = velocityY > 0 ? 1 : -1;
+ }
+ int finalPosition = Math.max(
+ 0, Math.min(getAdapter().getItemCount() - 1, currentPosition + delta));
+ smoothScrollToPosition(finalPosition);
+ return true;
+ }
+
+ public void smoothScrollToPosition(int position, RecyclerView.SmoothScroller smoothScroller) {
+ LayoutManager layoutManager = (LayoutManager) getLayoutManager();
+ layoutManager.setCustomSmoothScroller(smoothScroller);
+ smoothScrollToPosition(position);
+ layoutManager.clearCustomSmoothScroller();
+ }
+
+ @Override
+ public ViewHolder getChildViewHolder(View child) {
+ return (ViewHolder) super.getChildViewHolder(child);
+ }
+
+ /**
+ * Adds a listener that will be called when the user taps on the WearableListView or its items.
+ */
+ public void setClickListener(ClickListener clickListener) {
+ mClickListener = clickListener;
+ }
+
+ /**
+ * Adds a listener that will be called when the user drags the top element below its allowed
+ * bottom position.
+ *
+ * @hide
+ */
+ public void setOverScrollListener(OnOverScrollListener listener) {
+ mOverScrollListener = listener;
+ }
+
+ private int findCenterViewIndex() {
+ // TODO(gruszczy): This could be easily optimized, so that we stop looking when we the
+ // distance starts growing again, instead of finding the closest. It would safe half of
+ // the loop.
+ int count = getChildCount();
+ int index = -1;
+ int closest = Integer.MAX_VALUE;
+ int centerY = getCenterYPos(this);
+ for (int i = 0; i < count; ++i) {
+ final View child = getChildAt(i);
+ int childCenterY = getTop() + getCenterYPos(child);
+ final int distance = Math.abs(centerY - childCenterY);
+ if (distance < closest) {
+ closest = distance;
+ index = i;
+ }
+ }
+ if (index == -1) {
+ throw new IllegalStateException("Can't find central view.");
+ }
+ return index;
+ }
+
+ private static int getCenterYPos(View v) {
+ return v.getTop() + v.getPaddingTop() + getAdjustedHeight(v) / 2;
+ }
+
+ private void handleTouchUp(MotionEvent event, int scrollState) {
+ if (mCanClick && event != null && checkForTap(event)) {
+ Handler handler = getHandler();
+ if (handler != null) {
+ handler.postDelayed(mReleasedRunnable, ViewConfiguration.getTapTimeout());
+ }
+ return;
+ }
+
+ if (scrollState != RecyclerView.SCROLL_STATE_IDLE) {
+ // We are flinging, so let's not start animations just yet. Instead we will start them
+ // when the fling finishes.
+ return;
+ }
+
+ if (isOverScrolling()) {
+ mOverScrollListener.onOverScroll();
+ } else {
+ animateToCenter();
+ }
+ }
+
+ private boolean isOverScrolling() {
+ return getChildCount() > 0
+ // If first view top was below the central top, it means it was never centered.
+ // Don't allow overscroll, otherwise a simple touch (instead of a drag) will be
+ // enough to trigger overscroll.
+ && mStartFirstTop <= getCentralViewTop()
+ && getChildAt(0).getTop() >= getTopViewMaxTop()
+ && mOverScrollListener != null;
+ }
+
+ private int getTopViewMaxTop() {
+ return getHeight() / 2;
+ }
+
+ private int getItemHeight() {
+ // Round up so that the screen is fully occupied by 3 items.
+ return getAdjustedHeight() / THIRD + 1;
+ }
+
+ /**
+ * Returns top of the central {@code View} in the list when such view is fully centered.
+ *
+ * This is a more or a less a static value that you can use to align other views with the
+ * central one.
+ */
+ public int getCentralViewTop() {
+ return getPaddingTop() + getItemHeight();
+ }
+
+ /**
+ * Automatically starts an animation that snaps the list to center on the element closest to the
+ * middle.
+ */
+ public void animateToCenter() {
+ final int index = findCenterViewIndex();
+ final View child = getChildAt(index);
+ final int scrollToMiddle = getCentralViewTop() - child.getTop();
+ startScrollAnimation(scrollToMiddle, CENTERING_ANIMATION_DURATION_MS, 0,
+ new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (!wasCanceled()) {
+ mCanClick = true;
+ }
+ }
+ });
+ }
+
+ /**
+ * Animate the list so that the first view is back to its initial position.
+ * @param endAction Action to execute when the animation is done.
+ * @hide
+ */
+ public void animateToInitialPosition(final Runnable endAction) {
+ final View child = getChildAt(0);
+ final int scrollToMiddle = getCentralViewTop() + mInitialOffset - child.getTop();
+ startScrollAnimation(scrollToMiddle, CENTERING_ANIMATION_DURATION_MS, 0,
+ new SimpleAnimatorListener() {
+ @Override
+ public void onAnimationEnd(Animator animator) {
+ if (endAction != null) {
+ endAction.run();
+ }
+ }
+ });
+ }
+
+ private void handleTouchDown(MotionEvent event) {
+ if (mCanClick) {
+ mTapPositionX = (int) event.getX();
+ mTapPositionY = (int) event.getY();
+ float rawY = event.getRawY();
+ computeTapRegions(mTapRegions);
+ if (rawY > mTapRegions[0] && rawY < mTapRegions[1]) {
+ View view = getChildAt(findCenterViewIndex());
+ if (view instanceof OnCenterProximityListener) {
+ Handler handler = getHandler();
+ if (handler != null) {
+ handler.removeCallbacks(mReleasedRunnable);
+ handler.postDelayed(mPressedRunnable, ViewConfiguration.getTapTimeout());
+ }
+ }
+ }
+ }
+ }
+
+ private void setScrollVertically(int scroll) {
+ scrollBy(0, scroll - mLastScrollChange);
+ mLastScrollChange = scroll;
+ }
+
+ private int getAdjustedHeight() {
+ return getAdjustedHeight(this);
+ }
+
+ private static int getAdjustedHeight(View v) {
+ return v.getHeight() - v.getPaddingBottom() - v.getPaddingTop();
+ }
+
+ private void computeTapRegions(float[] tapRegions) {
+ mLocation[0] = mLocation[1] = 0;
+ getLocationOnScreen(mLocation);
+ int mScreenTop = mLocation[1];
+ int height = getHeight();
+ tapRegions[0] = mScreenTop + height * TOP_TAP_REGION_PERCENTAGE;
+ tapRegions[1] = mScreenTop + height * (1 - BOTTOM_TAP_REGION_PERCENTAGE);
+ }
+
+ /**
+ * Determines if, when there is only one item in the WearableListView, that the single item
+ * is laid out so that it's height fills the entire WearableListView.
+ */
+ public boolean getMaximizeSingleItem() {
+ return mMaximizeSingleItem;
+ }
+
+ /**
+ * When set to true, if there is only one item in the WearableListView, it will fill the entire
+ * WearableListView. When set to false, the default behavior will be used and the single item
+ * will fill only a third of the screen.
+ */
+ public void setMaximizeSingleItem(boolean maximizeSingleItem) {
+ mMaximizeSingleItem = maximizeSingleItem;
+ }
+
+ private void notifyChildrenAboutProximity(boolean animate) {
+ LayoutManager layoutManager = (LayoutManager) getLayoutManager();
+ int count = layoutManager.getChildCount();
+
+ if (count == 0) {
+ return;
+ }
+
+ int index = layoutManager.findCenterViewIndex();
+ for (int i = 0; i < count; ++i) {
+ final View view = layoutManager.getChildAt(i);
+ ViewHolder holder = getChildViewHolder(view);
+ holder.onCenterProximity(i == index, animate);
+ }
+ final int position = getChildViewHolder(getChildAt(index)).getPosition();
+ if (position != mPreviousCentral) {
+ for (OnScrollListener listener : mOnScrollListeners) {
+ listener.onCentralPositionChanged(position);
+ }
+ for (OnCentralPositionChangedListener listener :
+ mOnCentralPositionChangedListeners) {
+ listener.onCentralPositionChanged(position);
+ }
+ mPreviousCentral = position;
+ }
+ }
+
+ // TODO: Move this to a separate class, so it can't directly interact with the WearableListView.
+ private class LayoutManager extends RecyclerView.LayoutManager {
+ private int mFirstPosition;
+
+ private boolean mPushFirstHigher;
+
+ private int mAbsoluteScroll;
+
+ private boolean mUseOldViewTop = true;
+
+ private boolean mWasZoomedIn = false;
+
+ private RecyclerView.SmoothScroller mSmoothScroller;
+
+ private RecyclerView.SmoothScroller mDefaultSmoothScroller;
+
+ // We need to have another copy of the same method, because this one uses
+ // LayoutManager.getChildCount/getChildAt instead of View.getChildCount/getChildAt and
+ // they return different values.
+ private int findCenterViewIndex() {
+ // TODO(gruszczy): This could be easily optimized, so that we stop looking when we the
+ // distance starts growing again, instead of finding the closest. It would safe half of
+ // the loop.
+ int count = getChildCount();
+ int index = -1;
+ int closest = Integer.MAX_VALUE;
+ int centerY = getCenterYPos(WearableListView.this);
+ for (int i = 0; i < count; ++i) {
+ final View child = getLayoutManager().getChildAt(i);
+ int childCenterY = getTop() + getCenterYPos(child);
+ final int distance = Math.abs(centerY - childCenterY);
+ if (distance < closest) {
+ closest = distance;
+ index = i;
+ }
+ }
+ if (index == -1) {
+ throw new IllegalStateException("Can't find central view.");
+ }
+ return index;
+ }
+
+ @Override
+ public void onLayoutChildren(RecyclerView.Recycler recycler, State state) {
+ final int parentBottom = getHeight() - getPaddingBottom();
+ // By default we assume this is the first run and the first element will be centered
+ // with optional initial offset.
+ int oldTop = getCentralViewTop() + mInitialOffset;
+ // Here we handle any other situation where we relayout or we want to achieve a
+ // specific layout of children.
+ if (mUseOldViewTop && getChildCount() > 0) {
+ // We are performing a relayout after we already had some children, because e.g. the
+ // contents of an adapter has changed. First we want to check, if the central item
+ // from before the layout is still here, because we want to preserve it.
+ int index = findCenterViewIndex();
+ int position = getPosition(getChildAt(index));
+ if (position == NO_POSITION) {
+ // Central item was removed. Let's find the first surviving item and use it
+ // as an anchor.
+ for (int i = 0, N = getChildCount(); index + i < N || index - i >= 0; ++i) {
+ View child = getChildAt(index + i);
+ if (child != null) {
+ position = getPosition(child);
+ if (position != NO_POSITION) {
+ index = index + i;
+ break;
+ }
+ }
+ child = getChildAt(index - i);
+ if (child != null) {
+ position = getPosition(child);
+ if (position != NO_POSITION) {
+ index = index - i;
+ break;
+ }
+ }
+ }
+ }
+ if (position == NO_POSITION) {
+ // None of the children survives the relayout, let's just use the top of the
+ // first one.
+ oldTop = getChildAt(0).getTop();
+ int count = state.getItemCount();
+ // Lets first make sure that the first position is not above the last element,
+ // which can happen if elements were removed.
+ while (mFirstPosition >= count && mFirstPosition > 0) {
+ mFirstPosition--;
+ }
+ } else {
+ // Some of the children survived the relayout. We will keep it in its place,
+ // but go through previous children and maybe add them.
+ if (!mWasZoomedIn) {
+ // If we were previously zoomed-in on a single item, ignore this and just
+ // use the default value set above. Reasoning: if we are still zoomed-in,
+ // oldTop will be ignored when laying out the single child element. If we
+ // are no longer zoomed in, then we want to position items using the top
+ // of the single item as if the single item was not zoomed in, which is
+ // equal to the default value.
+ oldTop = getChildAt(index).getTop();
+ }
+ while (oldTop > getPaddingTop() && position > 0) {
+ position--;
+ oldTop -= getItemHeight();
+ }
+ if (position == 0 && oldTop > getCentralViewTop()) {
+ // We need to handle special case where the first, central item was removed
+ // and now the first element is hanging below, instead of being nicely
+ // centered.
+ oldTop = getCentralViewTop();
+ }
+ mFirstPosition = position;
+ }
+ } else if (mPushFirstHigher) {
+ // We are trying to position elements ourselves, so we force position of the first
+ // one.
+ oldTop = getCentralViewTop() - getItemHeight();
+ }
+
+ performLayoutChildren(recycler, state, parentBottom, oldTop);
+
+ // Since the content might have changed, we need to adjust the absolute scroll in case
+ // some elements have disappeared or were added.
+ if (getChildCount() == 0) {
+ setAbsoluteScroll(0);
+ } else {
+ View child = getChildAt(findCenterViewIndex());
+ setAbsoluteScroll(child.getTop() - getCentralViewTop() + getPosition(child) *
+ getItemHeight());
+ }
+
+ mUseOldViewTop = true;
+ mPushFirstHigher = false;
+ }
+
+ private void performLayoutChildren(Recycler recycler, State state, int parentBottom,
+ int top) {
+ detachAndScrapAttachedViews(recycler);
+
+ if (mMaximizeSingleItem && state.getItemCount() == 1) {
+ performLayoutOneChild(recycler, parentBottom);
+ mWasZoomedIn = true;
+ } else {
+ performLayoutMultipleChildren(recycler, state, parentBottom, top);
+ mWasZoomedIn = false;
+ }
+
+ if (getChildCount() > 0) {
+ post(mNotifyChildrenPostLayoutRunnable);
+ }
+ }
+
+ private void performLayoutOneChild(Recycler recycler, int parentBottom) {
+ final int right = getWidth() - getPaddingRight();
+ View v = recycler.getViewForPosition(getFirstPosition());
+ addView(v, 0);
+ measureZoomView(v);
+ v.layout(getPaddingLeft(), getPaddingTop(), right, parentBottom);
+ }
+
+ private void performLayoutMultipleChildren(Recycler recycler, State state, int parentBottom,
+ int top) {
+ int bottom;
+ final int left = getPaddingLeft();
+ final int right = getWidth() - getPaddingRight();
+ final int count = state.getItemCount();
+ // If we are laying out children with center element being different than the first, we
+ // need to start with previous child which appears half visible at the top.
+ for (int i = 0; getFirstPosition() + i < count; i++, top = bottom) {
+ if (top >= parentBottom) {
+ break;
+ }
+ View v = recycler.getViewForPosition(getFirstPosition() + i);
+ addView(v, i);
+ measureThirdView(v);
+ bottom = top + getItemHeight();
+ v.layout(left, top, right, bottom);
+ }
+ }
+
+ private void setAbsoluteScroll(int absoluteScroll) {
+ mAbsoluteScroll = absoluteScroll;
+ for (OnScrollListener listener : mOnScrollListeners) {
+ listener.onAbsoluteScrollChange(mAbsoluteScroll);
+ }
+ }
+
+ private void measureView(View v, int height) {
+ final LayoutParams lp = (LayoutParams) v.getLayoutParams();
+ final int widthSpec = getChildMeasureSpec(getWidth(),
+ getPaddingLeft() + getPaddingRight() + lp.leftMargin + lp.rightMargin, lp.width,
+ canScrollHorizontally());
+ final int heightSpec = getChildMeasureSpec(getHeight(),
+ getPaddingTop() + getPaddingBottom() + lp.topMargin + lp.bottomMargin,
+ height, canScrollVertically());
+ v.measure(widthSpec, heightSpec);
+ }
+
+ private void measureThirdView(View v) {
+ measureView(v, (int) (1 + (float) getHeight() / THIRD));
+ }
+
+ private void measureZoomView(View v) {
+ measureView(v, getHeight());
+ }
+
+ @Override
+ public RecyclerView.LayoutParams generateDefaultLayoutParams() {
+ return new RecyclerView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ }
+
+ @Override
+ public boolean canScrollVertically() {
+ // Disable vertical scrolling when zoomed.
+ return getItemCount() != 1 || !mWasZoomedIn;
+ }
+
+ @Override
+ public int scrollVerticallyBy(int dy, RecyclerView.Recycler recycler, State state) {
+ // TODO(gruszczy): This code is shit, needs to be rewritten.
+ if (getChildCount() == 0) {
+ return 0;
+ }
+ int scrolled = 0;
+ final int left = getPaddingLeft();
+ final int right = getWidth() - getPaddingRight();
+ if (dy < 0) {
+ while (scrolled > dy) {
+ final View topView = getChildAt(0);
+ if (getFirstPosition() > 0) {
+ final int hangingTop = Math.max(-topView.getTop(), 0);
+ final int scrollBy = Math.min(scrolled - dy, hangingTop);
+ scrolled -= scrollBy;
+ offsetChildrenVertical(scrollBy);
+ if (getFirstPosition() > 0 && scrolled > dy) {
+ mFirstPosition--;
+ View v = recycler.getViewForPosition(getFirstPosition());
+ addView(v, 0);
+ measureThirdView(v);
+ final int bottom = topView.getTop();
+ final int top = bottom - getItemHeight();
+ v.layout(left, top, right, bottom);
+ } else {
+ break;
+ }
+ } else {
+ mPushFirstHigher = false;
+ int maxScroll = mOverScrollListener!= null ?
+ getHeight() : getTopViewMaxTop();
+ final int scrollBy = Math.min(-dy + scrolled, maxScroll - topView.getTop());
+ scrolled -= scrollBy;
+ offsetChildrenVertical(scrollBy);
+ break;
+ }
+ }
+ } else if (dy > 0) {
+ final int parentHeight = getHeight();
+ while (scrolled < dy) {
+ final View bottomView = getChildAt(getChildCount() - 1);
+ if (state.getItemCount() > mFirstPosition + getChildCount()) {
+ final int hangingBottom =
+ Math.max(bottomView.getBottom() - parentHeight, 0);
+ final int scrollBy = -Math.min(dy - scrolled, hangingBottom);
+ scrolled -= scrollBy;
+ offsetChildrenVertical(scrollBy);
+ if (scrolled < dy) {
+ View v = recycler.getViewForPosition(mFirstPosition + getChildCount());
+ final int top = getChildAt(getChildCount() - 1).getBottom();
+ addView(v);
+ measureThirdView(v);
+ final int bottom = top + getItemHeight();
+ v.layout(left, top, right, bottom);
+ } else {
+ break;
+ }
+ } else {
+ final int scrollBy =
+ Math.max(-dy + scrolled, getHeight() / 2 - bottomView.getBottom());
+ scrolled -= scrollBy;
+ offsetChildrenVertical(scrollBy);
+ break;
+ }
+ }
+ }
+ recycleViewsOutOfBounds(recycler);
+ setAbsoluteScroll(mAbsoluteScroll + scrolled);
+ return scrolled;
+ }
+
+ @Override
+ public void scrollToPosition(int position) {
+ mUseOldViewTop = false;
+ if (position > 0) {
+ mFirstPosition = position - 1;
+ mPushFirstHigher = true;
+ } else {
+ mFirstPosition = position;
+ mPushFirstHigher = false;
+ }
+ requestLayout();
+ }
+
+ public void setCustomSmoothScroller(RecyclerView.SmoothScroller smoothScroller) {
+ mSmoothScroller = smoothScroller;
+ }
+
+ public void clearCustomSmoothScroller() {
+ mSmoothScroller = null;
+ }
+
+ public RecyclerView.SmoothScroller getDefaultSmoothScroller(RecyclerView recyclerView) {
+ if (mDefaultSmoothScroller == null) {
+ mDefaultSmoothScroller = new SmoothScroller(
+ recyclerView.getContext(), this);
+ }
+ return mDefaultSmoothScroller;
+ }
+ @Override
+ public void smoothScrollToPosition(RecyclerView recyclerView, State state,
+ int position) {
+ RecyclerView.SmoothScroller scroller = mSmoothScroller;
+ if (scroller == null) {
+ scroller = getDefaultSmoothScroller(recyclerView);
+ }
+ scroller.setTargetPosition(position);
+ startSmoothScroll(scroller);
+ }
+
+ private void recycleViewsOutOfBounds(RecyclerView.Recycler recycler) {
+ final int childCount = getChildCount();
+ final int parentWidth = getWidth();
+ // Here we want to use real height, so we don't remove views that are only visible in
+ // padded section.
+ final int parentHeight = getHeight();
+ boolean foundFirst = false;
+ int first = 0;
+ int last = 0;
+ for (int i = 0; i < childCount; i++) {
+ final View v = getChildAt(i);
+ if (v.hasFocus() || (v.getRight() >= 0 && v.getLeft() <= parentWidth &&
+ v.getBottom() >= 0 && v.getTop() <= parentHeight)) {
+ if (!foundFirst) {
+ first = i;
+ foundFirst = true;
+ }
+ last = i;
+ }
+ }
+ for (int i = childCount - 1; i > last; i--) {
+ removeAndRecycleViewAt(i, recycler);
+ }
+ for (int i = first - 1; i >= 0; i--) {
+ removeAndRecycleViewAt(i, recycler);
+ }
+ if (getChildCount() == 0) {
+ mFirstPosition = 0;
+ } else if (first > 0) {
+ mPushFirstHigher = true;
+ mFirstPosition += first;
+ }
+ }
+
+ public int getFirstPosition() {
+ return mFirstPosition;
+ }
+
+ @Override
+ public void onAdapterChanged(RecyclerView.Adapter oldAdapter,
+ RecyclerView.Adapter newAdapter) {
+ removeAllViews();
+ }
+ }
+
+ /**
+ * Interface for receiving callbacks when WearableListView children become or cease to be the
+ * central item.
+ */
+ public interface OnCenterProximityListener {
+ /**
+ * Called when this view becomes central item of the WearableListView.
+ *
+ * @param animate Whether you should animate your transition of the View to become the
+ * central item. If false, this is the initial setting and you should
+ * transition immediately.
+ */
+ void onCenterPosition(boolean animate);
+
+ /**
+ * Called when this view stops being the central item of the WearableListView.
+ * @param animate Whether you should animate your transition of the View to being
+ * non central item. If false, this is the initial setting and you should
+ * transition immediately.
+ */
+ void onNonCenterPosition(boolean animate);
+ }
+
+ /**
+ * Interface for listening for click events on WearableListView.
+ */
+ public interface ClickListener {
+ /**
+ * Called when the central child of the WearableListView is tapped.
+ * @param view View that was clicked.
+ */
+ public void onClick(ViewHolder view);
+
+ /**
+ * Called when the user taps the top third of the WearableListView and no item is present
+ * there. This can happen when you are in initial state and the first, top-most item of the
+ * WearableListView is centered.
+ */
+ public void onTopEmptyRegionClick();
+ }
+
+ /**
+ * @hide
+ */
+ public interface OnOverScrollListener {
+ public void onOverScroll();
+ }
+
+ /**
+ * Interface for listening to WearableListView content scrolling.
+ */
+ public interface OnScrollListener {
+ /**
+ * Called when the content is scrolled, reporting the relative scroll value.
+ * @param scroll Amount the content was scrolled. This is a delta from the previous
+ * position to the new position.
+ */
+ public void onScroll(int scroll);
+
+ /**
+ * Called when the content is scrolled, reporting the absolute scroll value.
+ *
+ * @deprecated BE ADVISED DO NOT USE THIS This might provide wrong values when contents
+ * of a RecyclerView change.
+ *
+ * @param scroll Absolute scroll position of the content inside the WearableListView.
+ */
+ @Deprecated
+ public void onAbsoluteScrollChange(int scroll);
+
+ /**
+ * Called when WearableListView's scroll state changes.
+ *
+ * @param scrollState The updated scroll state. One of {@link #SCROLL_STATE_IDLE},
+ * {@link #SCROLL_STATE_DRAGGING} or {@link #SCROLL_STATE_SETTLING}.
+ */
+ public void onScrollStateChanged(int scrollState);
+
+ /**
+ * Called when the central item of the WearableListView changes.
+ *
+ * @param centralPosition Position of the item in the Adapter.
+ */
+ public void onCentralPositionChanged(int centralPosition);
+ }
+
+ /**
+ * A listener interface that can be added to the WearableListView to get notified when the
+ * central item is changed.
+ */
+ public interface OnCentralPositionChangedListener {
+ /**
+ * Called when the central item of the WearableListView changes.
+ *
+ * @param centralPosition Position of the item in the Adapter.
+ */
+ void onCentralPositionChanged(int centralPosition);
+ }
+
+ /**
+ * Base class for adapters providing data for the WearableListView. For details refer to
+ * RecyclerView.Adapter.
+ */
+ public static abstract class Adapter extends RecyclerView.Adapter {
+ }
+
+ private static class SmoothScroller extends LinearSmoothScroller {
+
+ private static final float MILLISECONDS_PER_INCH = 100f;
+
+ private final LayoutManager mLayoutManager;
+
+ public SmoothScroller(Context context, WearableListView.LayoutManager manager) {
+ super(context);
+ mLayoutManager = manager;
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+ }
+
+ // TODO: (mindyp): when flinging, return the dydt that triggered the fling.
+ @Override
+ protected float calculateSpeedPerPixel(DisplayMetrics displayMetrics) {
+ return MILLISECONDS_PER_INCH / displayMetrics.densityDpi;
+ }
+
+ @Override
+ public int calculateDtToFit(int viewStart, int viewEnd, int boxStart, int boxEnd, int
+ snapPreference) {
+ // Snap to center.
+ return (boxStart + boxEnd) / 2 - (viewStart + viewEnd) / 2;
+ }
+
+ @Override
+ public PointF computeScrollVectorForPosition(int targetPosition) {
+ if (targetPosition < mLayoutManager.getFirstPosition()) {
+ return new PointF(0, -1);
+ } else {
+ return new PointF(0, 1);
+ }
+ }
+ }
+
+ /**
+ * Wrapper around items displayed in the list view. {@link .Adapter} must return objects that
+ * are instances of this class. Consider making the wrapped View implement
+ * {@link .OnCenterProximityListener} if you want to receive a callback when it becomes or
+ * ceases to be the central item in the WearableListView.
+ */
+ public static class ViewHolder extends RecyclerView.ViewHolder {
+ public ViewHolder(View itemView) {
+ super(itemView);
+ }
+
+ /**
+ * Called when the wrapped view is becoming or ceasing to be the central item of the
+ * WearableListView.
+ *
+ * Retained as protected for backwards compatibility.
+ *
+ * @hide
+ */
+ protected void onCenterProximity(boolean isCentralItem, boolean animate) {
+ if (!(itemView instanceof OnCenterProximityListener)) {
+ return;
+ }
+ OnCenterProximityListener item = (OnCenterProximityListener) itemView;
+ if (isCentralItem) {
+ item.onCenterPosition(animate);
+ } else {
+ item.onNonCenterPosition(animate);
+ }
+ }
+ }
+
+ private class SetScrollVerticallyProperty extends Property {
+ public SetScrollVerticallyProperty() {
+ super(Integer.class, "scrollVertically");
+ }
+
+ @Override
+ public Integer get(WearableListView wearableListView) {
+ return wearableListView.mLastScrollChange;
+ }
+
+ @Override
+ public void set(WearableListView wearableListView, Integer value) {
+ wearableListView.setScrollVertically(value);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
new file mode 100644
index 0000000000000..399cf1f5e0d63
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeleteStagedFileOnResult.java
@@ -0,0 +1,51 @@
+/*
+ * Copyright 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.annotation.Nullable;
+import android.app.Activity;
+import android.content.Intent;
+import android.os.Bundle;
+
+import java.io.File;
+
+/**
+ * Trampoline activity. Calls PackageInstallerActivity and deletes staged install file onResult.
+ */
+public class DeleteStagedFileOnResult extends Activity {
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState == null) {
+ Intent installIntent = new Intent(getIntent());
+ installIntent.setClass(this, PackageInstallerActivity.class);
+
+ installIntent.setFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivityForResult(installIntent, 0);
+ }
+ }
+
+ @Override
+ protected void onActivityResult(int requestCode, int resultCode, Intent data) {
+ File sourceFile = new File(getIntent().getData().getPath());
+ sourceFile.delete();
+
+ setResult(resultCode, data);
+ finish();
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
new file mode 100644
index 0000000000000..b54cad6f9276a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/DeviceUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller;
+
+import android.content.Context;
+import android.content.pm.PackageManager;
+import android.content.res.Configuration;
+
+public class DeviceUtils {
+ public static boolean isTelevision(Context context) {
+ int uiMode = context.getResources().getConfiguration().uiMode;
+ return (uiMode & Configuration.UI_MODE_TYPE_MASK) == Configuration.UI_MODE_TYPE_TELEVISION;
+ }
+
+ public static boolean isWear(final Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WATCH);
+ }
+
+ public static boolean isAuto(Context context) {
+ return context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_AUTOMOTIVE);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
new file mode 100644
index 0000000000000..0e62889c52aa4
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/EventResultPersister.java
@@ -0,0 +1,353 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInstaller;
+import android.os.AsyncTask;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.AtomicFile;
+import android.util.Log;
+import android.util.SparseArray;
+import android.util.Xml;
+
+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.FileOutputStream;
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+
+/**
+ * Persists results of events and calls back observers when a matching result arrives.
+ */
+class EventResultPersister {
+ private static final String LOG_TAG = EventResultPersister.class.getSimpleName();
+
+ /** Id passed to {@link #addObserver(int, EventResultObserver)} to generate new id */
+ static final int GENERATE_NEW_ID = Integer.MIN_VALUE;
+
+ /**
+ * The extra with the id to set in the intent delivered to
+ * {@link #onEventReceived(Context, Intent)}
+ */
+ static final String EXTRA_ID = "EventResultPersister.EXTRA_ID";
+
+ /** Persisted state of this object */
+ private final AtomicFile mResultsFile;
+
+ private final Object mLock = new Object();
+
+ /** Currently stored but not yet called back results (install id -> status, status message) */
+ private final SparseArray mResults = new SparseArray<>();
+
+ /** Currently registered, not called back observers (install id -> observer) */
+ private final SparseArray mObservers = new SparseArray<>();
+
+ /** Always increasing counter for install event ids */
+ private int mCounter;
+
+ /** If a write that will persist the state is scheduled */
+ private boolean mIsPersistScheduled;
+
+ /** If the state was changed while the data was being persisted */
+ private boolean mIsPersistingStateValid;
+
+ /**
+ * @return a new event id.
+ */
+ public int getNewId() throws OutOfIdsException {
+ synchronized (mLock) {
+ if (mCounter == Integer.MAX_VALUE) {
+ throw new OutOfIdsException();
+ }
+
+ mCounter++;
+ writeState();
+
+ return mCounter - 1;
+ }
+ }
+
+ /** Call back when a result is received. Observer is removed when onResult it called. */
+ interface EventResultObserver {
+ void onResult(int status, int legacyStatus, @Nullable String message);
+ }
+
+ /**
+ * Progress parser to the next element.
+ *
+ * @param parser The parser to progress
+ */
+ private static void nextElement(@NonNull XmlPullParser parser)
+ throws XmlPullParserException, IOException {
+ int type;
+ do {
+ type = parser.next();
+ } while (type != XmlPullParser.START_TAG && type != XmlPullParser.END_DOCUMENT);
+ }
+
+ /**
+ * Read an int attribute from the current element
+ *
+ * @param parser The parser to read from
+ * @param name The attribute name to read
+ *
+ * @return The value of the attribute
+ */
+ private static int readIntAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+ return Integer.parseInt(parser.getAttributeValue(null, name));
+ }
+
+ /**
+ * Read an String attribute from the current element
+ *
+ * @param parser The parser to read from
+ * @param name The attribute name to read
+ *
+ * @return The value of the attribute or null if the attribute is not set
+ */
+ private static String readStringAttribute(@NonNull XmlPullParser parser, @NonNull String name) {
+ return parser.getAttributeValue(null, name);
+ }
+
+ /**
+ * Read persisted state.
+ *
+ * @param resultFile The file the results are persisted in
+ */
+ EventResultPersister(@NonNull File resultFile) {
+ mResultsFile = new AtomicFile(resultFile);
+ mCounter = GENERATE_NEW_ID + 1;
+
+ try (FileInputStream stream = mResultsFile.openRead()) {
+ XmlPullParser parser = Xml.newPullParser();
+ parser.setInput(stream, StandardCharsets.UTF_8.name());
+
+ nextElement(parser);
+ while (parser.getEventType() != XmlPullParser.END_DOCUMENT) {
+ String tagName = parser.getName();
+ if ("results".equals(tagName)) {
+ mCounter = readIntAttribute(parser, "counter");
+ } else if ("result".equals(tagName)) {
+ int id = readIntAttribute(parser, "id");
+ int status = readIntAttribute(parser, "status");
+ int legacyStatus = readIntAttribute(parser, "legacyStatus");
+ String statusMessage = readStringAttribute(parser, "statusMessage");
+
+ if (mResults.get(id) != null) {
+ throw new Exception("id " + id + " has two results");
+ }
+
+ mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
+ } else {
+ throw new Exception("unexpected tag");
+ }
+
+ nextElement(parser);
+ }
+ } catch (Exception e) {
+ mResults.clear();
+ writeState();
+ }
+ }
+
+ /**
+ * Add a result. If the result is an pending user action, execute the pending user action
+ * directly and do not queue a result.
+ *
+ * @param context The context the event was received in
+ * @param intent The intent the activity received
+ */
+ void onEventReceived(@NonNull Context context, @NonNull Intent intent) {
+ int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
+
+ if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+ context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+
+ return;
+ }
+
+ int id = intent.getIntExtra(EXTRA_ID, 0);
+ String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
+
+ EventResultObserver observerToCall = null;
+ synchronized (mLock) {
+ int numObservers = mObservers.size();
+ for (int i = 0; i < numObservers; i++) {
+ if (mObservers.keyAt(i) == id) {
+ observerToCall = mObservers.valueAt(i);
+ mObservers.removeAt(i);
+
+ break;
+ }
+ }
+
+ if (observerToCall != null) {
+ observerToCall.onResult(status, legacyStatus, statusMessage);
+ } else {
+ mResults.put(id, new EventResult(status, legacyStatus, statusMessage));
+ writeState();
+ }
+ }
+ }
+
+ /**
+ * Persist current state. The persistence might be delayed.
+ */
+ private void writeState() {
+ synchronized (mLock) {
+ mIsPersistingStateValid = false;
+
+ if (!mIsPersistScheduled) {
+ mIsPersistScheduled = true;
+
+ AsyncTask.execute(() -> {
+ int counter;
+ SparseArray results;
+
+ while (true) {
+ // Take snapshot of state
+ synchronized (mLock) {
+ counter = mCounter;
+ results = mResults.clone();
+ mIsPersistingStateValid = true;
+ }
+
+ FileOutputStream stream = null;
+ try {
+ stream = mResultsFile.startWrite();
+ XmlSerializer serializer = Xml.newSerializer();
+ serializer.setOutput(stream, StandardCharsets.UTF_8.name());
+ serializer.startDocument(null, true);
+ serializer.setFeature(
+ "http://xmlpull.org/v1/doc/features.html#indent-output", true);
+ serializer.startTag(null, "results");
+ serializer.attribute(null, "counter", Integer.toString(counter));
+
+ int numResults = results.size();
+ for (int i = 0; i < numResults; i++) {
+ serializer.startTag(null, "result");
+ serializer.attribute(null, "id",
+ Integer.toString(results.keyAt(i)));
+ serializer.attribute(null, "status",
+ Integer.toString(results.valueAt(i).status));
+ serializer.attribute(null, "legacyStatus",
+ Integer.toString(results.valueAt(i).legacyStatus));
+ if (results.valueAt(i).message != null) {
+ serializer.attribute(null, "statusMessage",
+ results.valueAt(i).message);
+ }
+ serializer.endTag(null, "result");
+ }
+
+ serializer.endTag(null, "results");
+ serializer.endDocument();
+
+ mResultsFile.finishWrite(stream);
+ } catch (IOException e) {
+ if (stream != null) {
+ mResultsFile.failWrite(stream);
+ }
+
+ Log.e(LOG_TAG, "error writing results", e);
+ mResultsFile.delete();
+ }
+
+ // Check if there was changed state since we persisted. If so, we need to
+ // persist again.
+ synchronized (mLock) {
+ if (mIsPersistingStateValid) {
+ mIsPersistScheduled = false;
+ break;
+ }
+ }
+ }
+ });
+ }
+ }
+ }
+
+ /**
+ * Add an observer. If there is already an event for this id, call back inside of this call.
+ *
+ * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+ * @param observer The observer to call back.
+ *
+ * @return The id for this event
+ */
+ int addObserver(int id, @NonNull EventResultObserver observer)
+ throws OutOfIdsException {
+ synchronized (mLock) {
+ int resultIndex = -1;
+
+ if (id == GENERATE_NEW_ID) {
+ id = getNewId();
+ } else {
+ resultIndex = mResults.indexOfKey(id);
+ }
+
+ // Check if we can instantly call back
+ if (resultIndex >= 0) {
+ EventResult result = mResults.valueAt(resultIndex);
+
+ observer.onResult(result.status, result.legacyStatus, result.message);
+ mResults.removeAt(resultIndex);
+ writeState();
+ } else {
+ mObservers.put(id, observer);
+ }
+ }
+
+
+ return id;
+ }
+
+ /**
+ * Remove a observer.
+ *
+ * @param id The id the observer was added for
+ */
+ void removeObserver(int id) {
+ synchronized (mLock) {
+ mObservers.delete(id);
+ }
+ }
+
+ /**
+ * The status from an event.
+ */
+ private class EventResult {
+ public final int status;
+ public final int legacyStatus;
+ @Nullable public final String message;
+
+ private EventResult(int status, int legacyStatus, @Nullable String message) {
+ this.status = status;
+ this.legacyStatus = legacyStatus;
+ this.message = message;
+ }
+ }
+
+ class OutOfIdsException extends Exception {}
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
new file mode 100644
index 0000000000000..b7d32e9dcf936
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallEventReceiver.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import androidx.annotation.NonNull;
+
+/**
+ * Receives install events and perists them using a {@link EventResultPersister}.
+ */
+public class InstallEventReceiver extends BroadcastReceiver {
+ private static final Object sLock = new Object();
+ private static EventResultPersister sReceiver;
+
+ /**
+ * Get the event receiver persisting the results
+ *
+ * @return The event receiver.
+ */
+ @NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sReceiver == null) {
+ sReceiver = new EventResultPersister(
+ TemporaryFileManager.getInstallStateFile(context));
+ }
+ }
+
+ return sReceiver;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ getReceiver(context).onEventReceived(context, intent);
+ }
+
+ /**
+ * Add an observer. If there is already an event for this id, call back inside of this call.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+ * @param observer The observer to call back.
+ *
+ * @return The id for this event
+ */
+ static int addObserver(@NonNull Context context, int id,
+ @NonNull EventResultPersister.EventResultObserver observer)
+ throws EventResultPersister.OutOfIdsException {
+ return getReceiver(context).addObserver(id, observer);
+ }
+
+ /**
+ * Remove a observer.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer was added for
+ */
+ static void removeObserver(@NonNull Context context, int id) {
+ getReceiver(context).removeObserver(id);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
new file mode 100644
index 0000000000000..56dc71d5fa65a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallFailed.java
@@ -0,0 +1,161 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.widget.TextView;
+
+import java.io.File;
+
+/**
+ * Installation failed: Return status code to the caller or display failure UI to user
+ */
+public class InstallFailed extends Activity {
+ private static final String LOG_TAG = InstallFailed.class.getSimpleName();
+
+ /** Label of the app that failed to install */
+ private CharSequence mLabel;
+
+ /**
+ * Convert an package installer status code into the user friendly label.
+ *
+ * @param statusCode The status code from the package installer.
+ *
+ * @return The user friendly label for the status code
+ */
+ private int getExplanationFromErrorCode(int statusCode) {
+ Log.d(LOG_TAG, "Installation status code: " + statusCode);
+
+ switch (statusCode) {
+ case PackageInstaller.STATUS_FAILURE_BLOCKED:
+ return R.string.install_failed_blocked;
+ case PackageInstaller.STATUS_FAILURE_CONFLICT:
+ return R.string.install_failed_conflict;
+ case PackageInstaller.STATUS_FAILURE_INCOMPATIBLE:
+ return R.string.install_failed_incompatible;
+ case PackageInstaller.STATUS_FAILURE_INVALID:
+ return R.string.install_failed_invalid_apk;
+ default:
+ return R.string.install_failed;
+ }
+ }
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ int statusCode = getIntent().getIntExtra(PackageInstaller.EXTRA_STATUS,
+ PackageInstaller.STATUS_FAILURE);
+
+ if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ int legacyStatus = getIntent().getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS,
+ PackageManager.INSTALL_FAILED_INTERNAL_ERROR);
+
+ // Return result if requested
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
+ setResult(Activity.RESULT_FIRST_USER, result);
+ finish();
+ } else {
+ Intent intent = getIntent();
+ ApplicationInfo appInfo = intent
+ .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ Uri packageURI = intent.getData();
+
+ setContentView(R.layout.install_failed);
+
+ // Set header icon and title
+ PackageUtil.AppSnippet as;
+ PackageManager pm = getPackageManager();
+
+ if ("package".equals(packageURI.getScheme())) {
+ as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
+ pm.getApplicationIcon(appInfo));
+ } else {
+ final File sourceFile = new File(packageURI.getPath());
+ as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+ }
+
+ // Store label for dialog
+ mLabel = as.label;
+
+ PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
+
+ // Show out of space dialog if needed
+ if (statusCode == PackageInstaller.STATUS_FAILURE_STORAGE) {
+ (new OutOfSpaceDialog()).show(getFragmentManager(), "outofspace");
+ }
+
+ // Get status messages
+ ((TextView) findViewById(R.id.simple_status)).setText(
+ getExplanationFromErrorCode(statusCode));
+
+ // Set up "done" button
+ findViewById(R.id.done_button).setOnClickListener(view -> finish());
+ }
+ }
+
+ /**
+ * Dialog shown when we ran out of space during installation. This contains a link to the
+ * "manage applications" settings page.
+ */
+ public static class OutOfSpaceDialog extends DialogFragment {
+ private InstallFailed mActivity;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+
+ mActivity = (InstallFailed) context;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(mActivity)
+ .setTitle(R.string.out_of_space_dlg_title)
+ .setMessage(getString(R.string.out_of_space_dlg_text, mActivity.mLabel))
+ .setPositiveButton(R.string.manage_applications, (dialog, which) -> {
+ // launch manage applications
+ Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
+ startActivity(intent);
+ mActivity.finish();
+ })
+ .setNegativeButton(R.string.cancel, (dialog, which) -> mActivity.finish())
+ .create();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+
+ mActivity.finish();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
new file mode 100755
index 0000000000000..22b1336c2998b
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallInstalling.java
@@ -0,0 +1,413 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import static android.content.pm.PackageInstaller.SessionParams.UID_UNKNOWN;
+
+import android.app.Activity;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import android.util.Log;
+import android.widget.Button;
+import android.widget.ProgressBar;
+
+import androidx.annotation.Nullable;
+
+import com.android.internal.content.PackageHelper;
+
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Send package to the package manager and handle results from package manager. Once the
+ * installation succeeds, start {@link InstallSuccess} or {@link InstallFailed}.
+ *
This has two phases: First send the data to the package manager, then wait until the package
+ * manager processed the result.
+ */
+public class InstallInstalling extends Activity {
+ private static final String LOG_TAG = InstallInstalling.class.getSimpleName();
+
+ private static final String SESSION_ID = "com.android.packageinstaller.SESSION_ID";
+ private static final String INSTALL_ID = "com.android.packageinstaller.INSTALL_ID";
+
+ private static final String BROADCAST_ACTION =
+ "com.android.packageinstaller.ACTION_INSTALL_COMMIT";
+
+ /** Listens to changed to the session and updates progress bar */
+ private PackageInstaller.SessionCallback mSessionCallback;
+
+ /** Task that sends the package to the package installer */
+ private InstallingAsyncTask mInstallingTask;
+
+ /** Id of the session to install the package */
+ private int mSessionId;
+
+ /** Id of the install event we wait for */
+ private int mInstallId;
+
+ /** URI of package to install */
+ private Uri mPackageURI;
+
+ /** The button that can cancel this dialog */
+ private Button mCancelButton;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.install_installing);
+
+ ApplicationInfo appInfo = getIntent()
+ .getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ mPackageURI = getIntent().getData();
+
+ if ("package".equals(mPackageURI.getScheme())) {
+ try {
+ getPackageManager().installExistingPackage(appInfo.packageName);
+ launchSuccess();
+ } catch (PackageManager.NameNotFoundException e) {
+ launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ }
+ } else {
+ final File sourceFile = new File(mPackageURI.getPath());
+ PackageUtil.initSnippetForNewApp(this, PackageUtil.getAppSnippet(this, appInfo,
+ sourceFile), R.id.app_snippet);
+
+ if (savedInstanceState != null) {
+ mSessionId = savedInstanceState.getInt(SESSION_ID);
+ mInstallId = savedInstanceState.getInt(INSTALL_ID);
+
+ // Reregister for result; might instantly call back if result was delivered while
+ // activity was destroyed
+ try {
+ InstallEventReceiver.addObserver(this, mInstallId,
+ this::launchFinishBasedOnResult);
+ } catch (EventResultPersister.OutOfIdsException e) {
+ // Does not happen
+ }
+ } else {
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.installFlags = PackageManager.INSTALL_FULL_APP;
+ params.referrerUri = getIntent().getParcelableExtra(Intent.EXTRA_REFERRER);
+ params.originatingUri = getIntent()
+ .getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+ params.originatingUid = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+ UID_UNKNOWN);
+ params.installerPackageName =
+ getIntent().getStringExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+
+ File file = new File(mPackageURI.getPath());
+ try {
+ PackageParser.PackageLite pkg = PackageParser.parsePackageLite(file, 0);
+ params.setAppPackageName(pkg.packageName);
+ params.setInstallLocation(pkg.installLocation);
+ params.setSize(
+ PackageHelper.calculateInstalledSize(pkg, false, params.abiOverride));
+ } catch (PackageParser.PackageParserException e) {
+ Log.e(LOG_TAG, "Cannot parse package " + file + ". Assuming defaults.");
+ Log.e(LOG_TAG,
+ "Cannot calculate installed size " + file + ". Try only apk size.");
+ params.setSize(file.length());
+ } catch (IOException e) {
+ Log.e(LOG_TAG,
+ "Cannot calculate installed size " + file + ". Try only apk size.");
+ params.setSize(file.length());
+ }
+
+ try {
+ mInstallId = InstallEventReceiver
+ .addObserver(this, EventResultPersister.GENERATE_NEW_ID,
+ this::launchFinishBasedOnResult);
+ } catch (EventResultPersister.OutOfIdsException e) {
+ launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ }
+
+ try {
+ mSessionId = getPackageManager().getPackageInstaller().createSession(params);
+ } catch (IOException e) {
+ launchFailure(PackageManager.INSTALL_FAILED_INTERNAL_ERROR, null);
+ }
+ }
+
+ mCancelButton = (Button) findViewById(R.id.cancel_button);
+
+ mCancelButton.setOnClickListener(view -> {
+ if (mInstallingTask != null) {
+ mInstallingTask.cancel(true);
+ }
+
+ if (mSessionId > 0) {
+ getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+ mSessionId = 0;
+ }
+
+ setResult(RESULT_CANCELED);
+ finish();
+ });
+
+ mSessionCallback = new InstallSessionCallback();
+ }
+ }
+
+ /**
+ * Launch the "success" version of the final package installer dialog
+ */
+ private void launchSuccess() {
+ Intent successIntent = new Intent(getIntent());
+ successIntent.setClass(this, InstallSuccess.class);
+ successIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+ startActivity(successIntent);
+ finish();
+ }
+
+ /**
+ * Launch the "failure" version of the final package installer dialog
+ *
+ * @param legacyStatus The status as used internally in the package manager.
+ * @param statusMessage The status description.
+ */
+ private void launchFailure(int legacyStatus, String statusMessage) {
+ Intent failureIntent = new Intent(getIntent());
+ failureIntent.setClass(this, InstallFailed.class);
+ failureIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ failureIntent.putExtra(PackageInstaller.EXTRA_LEGACY_STATUS, legacyStatus);
+ failureIntent.putExtra(PackageInstaller.EXTRA_STATUS_MESSAGE, statusMessage);
+
+ startActivity(failureIntent);
+ finish();
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ getPackageManager().getPackageInstaller().registerSessionCallback(mSessionCallback);
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // This is the first onResume in a single life of the activity
+ if (mInstallingTask == null) {
+ PackageInstaller installer = getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionInfo sessionInfo = installer.getSessionInfo(mSessionId);
+
+ if (sessionInfo != null && !sessionInfo.isActive()) {
+ mInstallingTask = new InstallingAsyncTask();
+ mInstallingTask.execute();
+ } else {
+ // we will receive a broadcast when the install is finished
+ mCancelButton.setEnabled(false);
+ setFinishOnTouchOutside(false);
+ }
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(SESSION_ID, mSessionId);
+ outState.putInt(INSTALL_ID, mInstallId);
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mCancelButton.isEnabled()) {
+ super.onBackPressed();
+ }
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ getPackageManager().getPackageInstaller().unregisterSessionCallback(mSessionCallback);
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mInstallingTask != null) {
+ mInstallingTask.cancel(true);
+ synchronized (mInstallingTask) {
+ while (!mInstallingTask.isDone) {
+ try {
+ mInstallingTask.wait();
+ } catch (InterruptedException e) {
+ Log.i(LOG_TAG, "Interrupted while waiting for installing task to cancel",
+ e);
+ }
+ }
+ }
+ }
+
+ InstallEventReceiver.removeObserver(this, mInstallId);
+
+ super.onDestroy();
+ }
+
+ /**
+ * Launch the appropriate finish activity (success or failed) for the installation result.
+ *
+ * @param statusCode The installation result.
+ * @param legacyStatus The installation as used internally in the package manager.
+ * @param statusMessage The detailed installation result.
+ */
+ private void launchFinishBasedOnResult(int statusCode, int legacyStatus, String statusMessage) {
+ if (statusCode == PackageInstaller.STATUS_SUCCESS) {
+ launchSuccess();
+ } else {
+ launchFailure(legacyStatus, statusMessage);
+ }
+ }
+
+
+ private class InstallSessionCallback extends PackageInstaller.SessionCallback {
+ @Override
+ public void onCreated(int sessionId) {
+ // empty
+ }
+
+ @Override
+ public void onBadgingChanged(int sessionId) {
+ // empty
+ }
+
+ @Override
+ public void onActiveChanged(int sessionId, boolean active) {
+ // empty
+ }
+
+ @Override
+ public void onProgressChanged(int sessionId, float progress) {
+ if (sessionId == mSessionId) {
+ ProgressBar progressBar = (ProgressBar)findViewById(R.id.progress_bar);
+ progressBar.setMax(Integer.MAX_VALUE);
+ progressBar.setProgress((int) (Integer.MAX_VALUE * progress));
+ }
+ }
+
+ @Override
+ public void onFinished(int sessionId, boolean success) {
+ // empty, finish is handled by InstallResultReceiver
+ }
+ }
+
+ /**
+ * Send the package to the package installer and then register a event result observer that
+ * will call {@link #launchFinishBasedOnResult(int, int, String)}
+ */
+ private final class InstallingAsyncTask extends AsyncTask {
+ volatile boolean isDone;
+
+ @Override
+ protected PackageInstaller.Session doInBackground(Void... params) {
+ PackageInstaller.Session session;
+ try {
+ session = getPackageManager().getPackageInstaller().openSession(mSessionId);
+ } catch (IOException e) {
+ return null;
+ }
+
+ session.setStagingProgress(0);
+
+ try {
+ File file = new File(mPackageURI.getPath());
+
+ try (InputStream in = new FileInputStream(file)) {
+ long sizeBytes = file.length();
+ try (OutputStream out = session
+ .openWrite("PackageInstaller", 0, sizeBytes)) {
+ byte[] buffer = new byte[1024 * 1024];
+ while (true) {
+ int numRead = in.read(buffer);
+
+ if (numRead == -1) {
+ session.fsync(out);
+ break;
+ }
+
+ if (isCancelled()) {
+ session.close();
+ break;
+ }
+
+ out.write(buffer, 0, numRead);
+ if (sizeBytes > 0) {
+ float fraction = ((float) numRead / (float) sizeBytes);
+ session.addProgress(fraction);
+ }
+ }
+ }
+ }
+
+ return session;
+ } catch (IOException | SecurityException e) {
+ Log.e(LOG_TAG, "Could not write package", e);
+
+ session.close();
+
+ return null;
+ } finally {
+ synchronized (this) {
+ isDone = true;
+ notifyAll();
+ }
+ }
+ }
+
+ @Override
+ protected void onPostExecute(PackageInstaller.Session session) {
+ if (session != null) {
+ Intent broadcastIntent = new Intent(BROADCAST_ACTION);
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ broadcastIntent.setPackage(getPackageName());
+ broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mInstallId);
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(
+ InstallInstalling.this,
+ mInstallId,
+ broadcastIntent,
+ PendingIntent.FLAG_UPDATE_CURRENT);
+
+ session.commit(pendingIntent.getIntentSender());
+ mCancelButton.setEnabled(false);
+ setFinishOnTouchOutside(false);
+ } else {
+ getPackageManager().getPackageInstaller().abandonSession(mSessionId);
+
+ if (!isCancelled()) {
+ launchFailure(PackageManager.INSTALL_FAILED_INVALID_APK, null);
+ }
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
new file mode 100644
index 0000000000000..bf72b65d8fb92
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStaging.java
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.net.Uri;
+import android.os.AsyncTask;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * If a package gets installed from an content URI this step loads the package and turns it into
+ * and installation from a file. Then it re-starts the installation as usual.
+ */
+public class InstallStaging extends Activity {
+ private static final String LOG_TAG = InstallStaging.class.getSimpleName();
+
+ private static final String STAGED_FILE = "STAGED_FILE";
+
+ /** Currently running task that loads the file from the content URI into a file */
+ private @Nullable StagingAsyncTask mStagingTask;
+
+ /** The file the package is in */
+ private @Nullable File mStagedFile;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setContentView(R.layout.install_staging);
+
+ if (savedInstanceState != null) {
+ mStagedFile = new File(savedInstanceState.getString(STAGED_FILE));
+
+ if (!mStagedFile.exists()) {
+ mStagedFile = null;
+ }
+ }
+
+ findViewById(R.id.cancel_button).setOnClickListener(view -> {
+ if (mStagingTask != null) {
+ mStagingTask.cancel(true);
+ }
+ setResult(RESULT_CANCELED);
+ finish();
+ });
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ // This is the first onResume in a single life of the activity
+ if (mStagingTask == null) {
+ // File does not exist, or became invalid
+ if (mStagedFile == null) {
+ // Create file delayed to be able to show error
+ try {
+ mStagedFile = TemporaryFileManager.getStagedFile(this);
+ } catch (IOException e) {
+ showError();
+ return;
+ }
+ }
+
+ mStagingTask = new StagingAsyncTask();
+ mStagingTask.execute(getIntent().getData());
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putString(STAGED_FILE, mStagedFile.getPath());
+ }
+
+ @Override
+ protected void onDestroy() {
+ if (mStagingTask != null) {
+ mStagingTask.cancel(true);
+ }
+
+ super.onDestroy();
+ }
+
+ /**
+ * Show an error message and set result as error.
+ */
+ private void showError() {
+ (new ErrorDialog()).showAllowingStateLoss(getFragmentManager(), "error");
+
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_APK);
+ setResult(RESULT_FIRST_USER, result);
+ }
+
+ /**
+ * Dialog for errors while staging.
+ */
+ public static class ErrorDialog extends DialogFragment {
+ private Activity mActivity;
+
+ @Override
+ public void onAttach(Context context) {
+ super.onAttach(context);
+
+ mActivity = (Activity) context;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog alertDialog = new AlertDialog.Builder(mActivity)
+ .setMessage(R.string.Parse_error_dlg_text)
+ .setPositiveButton(R.string.ok,
+ (dialog, which) -> mActivity.finish())
+ .create();
+ alertDialog.setCanceledOnTouchOutside(false);
+
+ return alertDialog;
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ super.onCancel(dialog);
+
+ mActivity.finish();
+ }
+ }
+
+ private final class StagingAsyncTask extends AsyncTask {
+ @Override
+ protected Boolean doInBackground(Uri... params) {
+ if (params == null || params.length <= 0) {
+ return false;
+ }
+ Uri packageUri = params[0];
+ try (InputStream in = getContentResolver().openInputStream(packageUri)) {
+ // Despite the comments in ContentResolver#openInputStream the returned stream can
+ // be null.
+ if (in == null) {
+ return false;
+ }
+
+ try (OutputStream out = new FileOutputStream(mStagedFile)) {
+ byte[] buffer = new byte[1024 * 1024];
+ int bytesRead;
+ while ((bytesRead = in.read(buffer)) >= 0) {
+ // Be nice and respond to a cancellation
+ if (isCancelled()) {
+ return false;
+ }
+ out.write(buffer, 0, bytesRead);
+ }
+ }
+ } catch (IOException | SecurityException | IllegalStateException e) {
+ Log.w(LOG_TAG, "Error staging apk from content URI", e);
+ return false;
+ }
+ return true;
+ }
+
+ @Override
+ protected void onPostExecute(Boolean success) {
+ if (success) {
+ // Now start the installation again from a file
+ Intent installIntent = new Intent(getIntent());
+ installIntent.setClass(InstallStaging.this, DeleteStagedFileOnResult.class);
+ installIntent.setData(Uri.fromFile(mStagedFile));
+
+ if (installIntent.getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ installIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ }
+
+ installIntent.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION);
+ startActivity(installIntent);
+
+ InstallStaging.this.finish();
+ } else {
+ showError();
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
new file mode 100644
index 0000000000000..96c6c2f621872
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallStart.java
@@ -0,0 +1,218 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.AppGlobals;
+import android.content.ContentResolver;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.ProviderInfo;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.RemoteException;
+import android.util.Log;
+
+import androidx.annotation.Nullable;
+
+/**
+ * Select which activity is the first visible activity of the installation and forward the intent to
+ * it.
+ */
+public class InstallStart extends Activity {
+ private static final String LOG_TAG = InstallStart.class.getSimpleName();
+
+ private static final String DOWNLOADS_AUTHORITY = "downloads";
+ private IPackageManager mIPackageManager;
+ private boolean mAbortInstall = false;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ mIPackageManager = AppGlobals.getPackageManager();
+ Intent intent = getIntent();
+ String callingPackage = getCallingPackage();
+
+ // If the activity was started via a PackageInstaller session, we retrieve the calling
+ // package from that session
+ int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+ if (callingPackage == null && sessionId != -1) {
+ PackageInstaller packageInstaller = getPackageManager().getPackageInstaller();
+ PackageInstaller.SessionInfo sessionInfo = packageInstaller.getSessionInfo(sessionId);
+ callingPackage = (sessionInfo != null) ? sessionInfo.getInstallerPackageName() : null;
+ }
+
+ final ApplicationInfo sourceInfo = getSourceInfo(callingPackage);
+ final int originatingUid = getOriginatingUid(sourceInfo);
+ boolean isTrustedSource = false;
+ if (sourceInfo != null
+ && (sourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED) != 0) {
+ isTrustedSource = intent.getBooleanExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, false);
+ }
+
+ if (!isTrustedSource && originatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
+ final int targetSdkVersion = getMaxTargetSdkVersionForUid(this, originatingUid);
+ if (targetSdkVersion < 0) {
+ Log.w(LOG_TAG, "Cannot get target sdk version for uid " + originatingUid);
+ // Invalid originating uid supplied. Abort install.
+ mAbortInstall = true;
+ } else if (targetSdkVersion >= Build.VERSION_CODES.O && !declaresAppOpPermission(
+ originatingUid, Manifest.permission.REQUEST_INSTALL_PACKAGES)) {
+ Log.e(LOG_TAG, "Requesting uid " + originatingUid + " needs to declare permission "
+ + Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ mAbortInstall = true;
+ }
+ }
+ if (mAbortInstall) {
+ setResult(RESULT_CANCELED);
+ finish();
+ return;
+ }
+
+ Intent nextActivity = new Intent(intent);
+ nextActivity.setFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+
+ // The the installation source as the nextActivity thinks this activity is the source, hence
+ // set the originating UID and sourceInfo explicitly
+ nextActivity.putExtra(PackageInstallerActivity.EXTRA_CALLING_PACKAGE, callingPackage);
+ nextActivity.putExtra(PackageInstallerActivity.EXTRA_ORIGINAL_SOURCE_INFO, sourceInfo);
+ nextActivity.putExtra(Intent.EXTRA_ORIGINATING_UID, originatingUid);
+
+ if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+ nextActivity.setClass(this, PackageInstallerActivity.class);
+ } else {
+ Uri packageUri = intent.getData();
+
+ if (packageUri != null && (packageUri.getScheme().equals(ContentResolver.SCHEME_FILE)
+ || packageUri.getScheme().equals(ContentResolver.SCHEME_CONTENT))) {
+ // Copy file to prevent it from being changed underneath this process
+ nextActivity.setClass(this, InstallStaging.class);
+ } else if (packageUri != null && packageUri.getScheme().equals(
+ PackageInstallerActivity.SCHEME_PACKAGE)) {
+ nextActivity.setClass(this, PackageInstallerActivity.class);
+ } else {
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT,
+ PackageManager.INSTALL_FAILED_INVALID_URI);
+ setResult(RESULT_FIRST_USER, result);
+
+ nextActivity = null;
+ }
+ }
+
+ if (nextActivity != null) {
+ startActivity(nextActivity);
+ }
+ finish();
+ }
+
+ private boolean declaresAppOpPermission(int uid, String permission) {
+ try {
+ final String[] packages = mIPackageManager.getAppOpPermissionPackages(permission);
+ if (packages == null) {
+ return false;
+ }
+ for (String packageName : packages) {
+ try {
+ if (uid == getPackageManager().getPackageUid(packageName, 0)) {
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
+ }
+ }
+ } catch (RemoteException rexc) {
+ // If remote package manager cannot be reached, install will likely fail anyway.
+ }
+ return false;
+ }
+
+ /**
+ * @return the ApplicationInfo for the installation source (the calling package), if available
+ */
+ private ApplicationInfo getSourceInfo(@Nullable String callingPackage) {
+ if (callingPackage != null) {
+ try {
+ return getPackageManager().getApplicationInfo(callingPackage, 0);
+ } catch (PackageManager.NameNotFoundException ex) {
+ // ignore
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Get the originating uid if possible, or
+ * {@link android.content.pm.PackageInstaller.SessionParams#UID_UNKNOWN} if not available
+ *
+ * @param sourceInfo The source of this installation
+ * @return The UID of the installation source or UID_UNKNOWN
+ */
+ private int getOriginatingUid(@Nullable ApplicationInfo sourceInfo) {
+ // The originating uid from the intent. We only trust/use this if it comes from either
+ // the document manager app or the downloads provider
+ final int uidFromIntent = getIntent().getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+ PackageInstaller.SessionParams.UID_UNKNOWN);
+
+ final int callingUid;
+ if (sourceInfo != null) {
+ callingUid = sourceInfo.uid;
+ } else {
+ try {
+ callingUid = ActivityManager.getService()
+ .getLaunchedFromUid(getActivityToken());
+ } catch (RemoteException ex) {
+ // Cannot reach ActivityManager. Aborting install.
+ Log.e(LOG_TAG, "Could not determine the launching uid.");
+ mAbortInstall = true;
+ return PackageInstaller.SessionParams.UID_UNKNOWN;
+ }
+ }
+ try {
+ if (mIPackageManager.checkUidPermission(Manifest.permission.MANAGE_DOCUMENTS,
+ callingUid) == PackageManager.PERMISSION_GRANTED) {
+ return uidFromIntent;
+ }
+ } catch (RemoteException rexc) {
+ // Ignore. Should not happen.
+ }
+ if (isSystemDownloadsProvider(callingUid)) {
+ return uidFromIntent;
+ }
+ // We don't trust uid from the intent. Use the calling uid instead.
+ return callingUid;
+ }
+
+ private boolean isSystemDownloadsProvider(int uid) {
+ final ProviderInfo downloadProviderPackage = getPackageManager().resolveContentProvider(
+ DOWNLOADS_AUTHORITY, 0);
+ if (downloadProviderPackage == null) {
+ // There seems to be no currently enabled downloads provider on the system.
+ return false;
+ }
+ final ApplicationInfo appInfo = downloadProviderPackage.applicationInfo;
+ return (appInfo.isSystemApp() && uid == appInfo.uid);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
new file mode 100644
index 0000000000000..ae902d3afc78b
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/InstallSuccess.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Activity;
+import android.content.ActivityNotFoundException;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.net.Uri;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.widget.Button;
+
+import java.io.File;
+import java.util.List;
+
+/**
+ * Finish installation: Return status code to the caller or display "success" UI to user
+ */
+public class InstallSuccess extends Activity {
+ private static final String LOG_TAG = InstallSuccess.class.getSimpleName();
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ // Return result if requested
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT, PackageManager.INSTALL_SUCCEEDED);
+ setResult(Activity.RESULT_OK, result);
+ finish();
+ } else {
+ Intent intent = getIntent();
+ ApplicationInfo appInfo =
+ intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ Uri packageURI = intent.getData();
+
+ setContentView(R.layout.install_success);
+
+ // Set header icon and title
+ PackageUtil.AppSnippet as;
+ PackageManager pm = getPackageManager();
+
+ if ("package".equals(packageURI.getScheme())) {
+ as = new PackageUtil.AppSnippet(pm.getApplicationLabel(appInfo),
+ pm.getApplicationIcon(appInfo));
+ } else {
+ File sourceFile = new File(packageURI.getPath());
+ as = PackageUtil.getAppSnippet(this, appInfo, sourceFile);
+ }
+
+ PackageUtil.initSnippetForNewApp(this, as, R.id.app_snippet);
+
+ // Set up "done" button
+ findViewById(R.id.done_button).setOnClickListener(view -> {
+ if (appInfo.packageName != null) {
+ Log.i(LOG_TAG, "Finished installing " + appInfo.packageName);
+ }
+ finish();
+ });
+
+ // Enable or disable "launch" button
+ Intent launchIntent = getPackageManager().getLaunchIntentForPackage(
+ appInfo.packageName);
+ boolean enabled = false;
+ if (launchIntent != null) {
+ List list = getPackageManager().queryIntentActivities(launchIntent,
+ 0);
+ if (list != null && list.size() > 0) {
+ enabled = true;
+ }
+ }
+
+ Button launchButton = (Button)findViewById(R.id.launch_button);
+ if (enabled) {
+ launchButton.setOnClickListener(view -> {
+ try {
+ startActivity(launchIntent);
+ } catch (ActivityNotFoundException | SecurityException e) {
+ Log.e(LOG_TAG, "Could not start activity", e);
+ }
+ finish();
+ });
+ } else {
+ launchButton.setEnabled(false);
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
new file mode 100644
index 0000000000000..83c91eaadd3c4
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerActivity.java
@@ -0,0 +1,770 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.packageinstaller;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.ActivityNotFoundException;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageUserState;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.Process;
+import android.os.RemoteException;
+import android.os.UserManager;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.widget.Button;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+
+import com.android.packageinstaller.permission.ui.OverlayTouchActivity;
+
+import java.io.File;
+
+/**
+ * This activity is launched when a new application is installed via side loading
+ * The package is first parsed and the user is notified of parse errors via a dialog.
+ * If the package is successfully parsed, the user is notified to turn on the install unknown
+ * applications setting. A memory check is made at this point and the user is notified of out
+ * of memory conditions if any. If the package is already existing on the device,
+ * a confirmation dialog (to replace the existing package) is presented to the user.
+ * Based on the user response the package is then installed by launching InstallAppConfirm
+ * sub activity. All state transitions are handled in this activity
+ */
+public class PackageInstallerActivity extends OverlayTouchActivity implements OnClickListener {
+ private static final String TAG = "PackageInstaller";
+
+ private static final int REQUEST_TRUST_EXTERNAL_SOURCE = 1;
+
+ static final String SCHEME_PACKAGE = "package";
+
+ static final String EXTRA_CALLING_PACKAGE = "EXTRA_CALLING_PACKAGE";
+ static final String EXTRA_ORIGINAL_SOURCE_INFO = "EXTRA_ORIGINAL_SOURCE_INFO";
+ private static final String ALLOW_UNKNOWN_SOURCES_KEY =
+ PackageInstallerActivity.class.getName() + "ALLOW_UNKNOWN_SOURCES_KEY";
+
+ private int mSessionId = -1;
+ private Uri mPackageURI;
+ private Uri mOriginatingURI;
+ private Uri mReferrerURI;
+ private int mOriginatingUid = PackageInstaller.SessionParams.UID_UNKNOWN;
+ private String mOriginatingPackage; // The package name corresponding to #mOriginatingUid
+
+ private boolean localLOGV = false;
+ PackageManager mPm;
+ IPackageManager mIpm;
+ AppOpsManager mAppOpsManager;
+ UserManager mUserManager;
+ PackageInstaller mInstaller;
+ PackageInfo mPkgInfo;
+ String mCallingPackage;
+ ApplicationInfo mSourceInfo;
+
+ // ApplicationInfo object primarily used for already existing applications
+ private ApplicationInfo mAppInfo = null;
+
+ // Buttons to indicate user acceptance
+ private Button mOk;
+ private Button mCancel;
+
+ private PackageUtil.AppSnippet mAppSnippet;
+
+ static final String PREFS_ALLOWED_SOURCES = "allowed_sources";
+
+ // Dialog identifiers used in showDialog
+ private static final int DLG_BASE = 0;
+ private static final int DLG_PACKAGE_ERROR = DLG_BASE + 2;
+ private static final int DLG_OUT_OF_SPACE = DLG_BASE + 3;
+ private static final int DLG_INSTALL_ERROR = DLG_BASE + 4;
+ private static final int DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER = DLG_BASE + 5;
+ private static final int DLG_ANONYMOUS_SOURCE = DLG_BASE + 6;
+ private static final int DLG_NOT_SUPPORTED_ON_WEAR = DLG_BASE + 7;
+ private static final int DLG_EXTERNAL_SOURCE_BLOCKED = DLG_BASE + 8;
+ private static final int DLG_INSTALL_APPS_RESTRICTED_FOR_USER = DLG_BASE + 9;
+
+ // If unknown sources are temporary allowed
+ private boolean mAllowUnknownSources;
+
+ // Would the mOk button be enabled if this activity would be resumed
+ private boolean mEnableOk = false;
+
+ private void startInstallConfirm() {
+ int msg;
+
+ if (mAppInfo != null) {
+ msg = (mAppInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0
+ ? R.string.install_confirm_question_update_system
+ : R.string.install_confirm_question_update;
+ } else {
+ // This is a new application with no permissions.
+ msg = R.string.install_confirm_question;
+ }
+
+ ((TextView) findViewById(R.id.install_confirm_question)).setText(msg);
+
+ mEnableOk = true;
+ mOk.setEnabled(true);
+ }
+
+ /**
+ * Replace any dialog shown by the dialog with the one for the given {@link #createDialog id}.
+ *
+ * @param id The dialog type to add
+ */
+ private void showDialogInner(int id) {
+ DialogFragment currentDialog =
+ (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+ if (currentDialog != null) {
+ currentDialog.dismissAllowingStateLoss();
+ }
+
+ DialogFragment newDialog = createDialog(id);
+ if (newDialog != null) {
+ newDialog.showAllowingStateLoss(getFragmentManager(), "dialog");
+ }
+ }
+
+ /**
+ * Create a new dialog.
+ *
+ * @param id The id of the dialog (determines dialog type)
+ *
+ * @return The dialog
+ */
+ private DialogFragment createDialog(int id) {
+ switch (id) {
+ case DLG_PACKAGE_ERROR:
+ return SimpleErrorDialog.newInstance(R.string.Parse_error_dlg_text);
+ case DLG_OUT_OF_SPACE:
+ return OutOfSpaceDialog.newInstance(
+ mPm.getApplicationLabel(mPkgInfo.applicationInfo));
+ case DLG_INSTALL_ERROR:
+ return InstallErrorDialog.newInstance(
+ mPm.getApplicationLabel(mPkgInfo.applicationInfo));
+ case DLG_NOT_SUPPORTED_ON_WEAR:
+ return NotSupportedOnWearDialog.newInstance();
+ case DLG_INSTALL_APPS_RESTRICTED_FOR_USER:
+ return SimpleErrorDialog.newInstance(
+ R.string.install_apps_user_restriction_dlg_text);
+ case DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER:
+ return SimpleErrorDialog.newInstance(
+ R.string.unknown_apps_user_restriction_dlg_text);
+ case DLG_EXTERNAL_SOURCE_BLOCKED:
+ return ExternalSourcesBlockedDialog.newInstance(mOriginatingPackage);
+ case DLG_ANONYMOUS_SOURCE:
+ return AnonymousSourceDialog.newInstance();
+ }
+ return null;
+ }
+
+ @Override
+ public void onActivityResult(int request, int result, Intent data) {
+ if (request == REQUEST_TRUST_EXTERNAL_SOURCE && result == RESULT_OK) {
+ // The user has just allowed this package to install other packages (via Settings).
+ mAllowUnknownSources = true;
+
+ // Log the fact that the app is requesting an install, and is now allowed to do it
+ // (before this point we could only log that it's requesting an install, but isn't
+ // allowed to do it yet).
+ int appOpCode =
+ AppOpsManager.permissionToOpCode(Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ mAppOpsManager.noteOpNoThrow(appOpCode, mOriginatingUid, mOriginatingPackage);
+
+ DialogFragment currentDialog =
+ (DialogFragment) getFragmentManager().findFragmentByTag("dialog");
+ if (currentDialog != null) {
+ currentDialog.dismissAllowingStateLoss();
+ }
+
+ initiateInstall();
+ } else {
+ finish();
+ }
+ }
+
+ private String getPackageNameForUid(int sourceUid) {
+ String[] packagesForUid = mPm.getPackagesForUid(sourceUid);
+ if (packagesForUid == null) {
+ return null;
+ }
+ if (packagesForUid.length > 1) {
+ if (mCallingPackage != null) {
+ for (String packageName : packagesForUid) {
+ if (packageName.equals(mCallingPackage)) {
+ return packageName;
+ }
+ }
+ }
+ Log.i(TAG, "Multiple packages found for source uid " + sourceUid);
+ }
+ return packagesForUid[0];
+ }
+
+ private boolean isInstallRequestFromUnknownSource(Intent intent) {
+ if (mCallingPackage != null && intent.getBooleanExtra(
+ Intent.EXTRA_NOT_UNKNOWN_SOURCE, false)) {
+ if (mSourceInfo != null) {
+ if ((mSourceInfo.privateFlags & ApplicationInfo.PRIVATE_FLAG_PRIVILEGED)
+ != 0) {
+ // Privileged apps can bypass unknown sources check if they want.
+ return false;
+ }
+ }
+ }
+ return true;
+ }
+
+ private void initiateInstall() {
+ String pkgName = mPkgInfo.packageName;
+ // Check if there is already a package on the device with this name
+ // but it has been renamed to something else.
+ String[] oldName = mPm.canonicalToCurrentPackageNames(new String[] { pkgName });
+ if (oldName != null && oldName.length > 0 && oldName[0] != null) {
+ pkgName = oldName[0];
+ mPkgInfo.packageName = pkgName;
+ mPkgInfo.applicationInfo.packageName = pkgName;
+ }
+ // Check if package is already installed. display confirmation dialog if replacing pkg
+ try {
+ // This is a little convoluted because we want to get all uninstalled
+ // apps, but this may include apps with just data, and if it is just
+ // data we still want to count it as "installed".
+ mAppInfo = mPm.getApplicationInfo(pkgName,
+ PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ if ((mAppInfo.flags&ApplicationInfo.FLAG_INSTALLED) == 0) {
+ mAppInfo = null;
+ }
+ } catch (NameNotFoundException e) {
+ mAppInfo = null;
+ }
+
+ startInstallConfirm();
+ }
+
+ void setPmResult(int pmResult) {
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT, pmResult);
+ setResult(pmResult == PackageManager.INSTALL_SUCCEEDED
+ ? RESULT_OK : RESULT_FIRST_USER, result);
+ }
+
+ @Override
+ protected void onCreate(Bundle icicle) {
+ super.onCreate(null);
+
+ if (icicle != null) {
+ mAllowUnknownSources = icicle.getBoolean(ALLOW_UNKNOWN_SOURCES_KEY);
+ }
+
+ mPm = getPackageManager();
+ mIpm = AppGlobals.getPackageManager();
+ mAppOpsManager = (AppOpsManager) getSystemService(Context.APP_OPS_SERVICE);
+ mInstaller = mPm.getPackageInstaller();
+ mUserManager = (UserManager) getSystemService(Context.USER_SERVICE);
+
+ final Intent intent = getIntent();
+
+ mCallingPackage = intent.getStringExtra(EXTRA_CALLING_PACKAGE);
+ mSourceInfo = intent.getParcelableExtra(EXTRA_ORIGINAL_SOURCE_INFO);
+ mOriginatingUid = intent.getIntExtra(Intent.EXTRA_ORIGINATING_UID,
+ PackageInstaller.SessionParams.UID_UNKNOWN);
+ mOriginatingPackage = (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN)
+ ? getPackageNameForUid(mOriginatingUid) : null;
+
+
+ final Uri packageUri;
+
+ if (PackageInstaller.ACTION_CONFIRM_INSTALL.equals(intent.getAction())) {
+ final int sessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1);
+ final PackageInstaller.SessionInfo info = mInstaller.getSessionInfo(sessionId);
+ if (info == null || !info.sealed || info.resolvedBaseCodePath == null) {
+ Log.w(TAG, "Session " + mSessionId + " in funky state; ignoring");
+ finish();
+ return;
+ }
+
+ mSessionId = sessionId;
+ packageUri = Uri.fromFile(new File(info.resolvedBaseCodePath));
+ mOriginatingURI = null;
+ mReferrerURI = null;
+ } else {
+ mSessionId = -1;
+ packageUri = intent.getData();
+ mOriginatingURI = intent.getParcelableExtra(Intent.EXTRA_ORIGINATING_URI);
+ mReferrerURI = intent.getParcelableExtra(Intent.EXTRA_REFERRER);
+ }
+
+ // if there's nothing to do, quietly slip into the ether
+ if (packageUri == null) {
+ Log.w(TAG, "Unspecified source");
+ setPmResult(PackageManager.INSTALL_FAILED_INVALID_URI);
+ finish();
+ return;
+ }
+
+ if (DeviceUtils.isWear(this)) {
+ showDialogInner(DLG_NOT_SUPPORTED_ON_WEAR);
+ return;
+ }
+
+ boolean wasSetUp = processPackageUri(packageUri);
+ if (!wasSetUp) {
+ return;
+ }
+
+ // load dummy layout with OK button disabled until we override this layout in
+ // startInstallConfirm
+ bindUi(R.layout.install_confirm);
+ checkIfAllowedAndInitiateInstall();
+ }
+
+ @Override
+ protected void onResume() {
+ super.onResume();
+
+ if (mOk != null) {
+ mOk.setEnabled(mEnableOk);
+ }
+ }
+
+ @Override
+ protected void onPause() {
+ super.onPause();
+
+ if (mOk != null) {
+ // Don't allow the install button to be clicked as there might be overlays
+ mOk.setEnabled(false);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(ALLOW_UNKNOWN_SOURCES_KEY, mAllowUnknownSources);
+ }
+
+ private void bindUi(int layout) {
+ setContentView(layout);
+
+ mOk = (Button) findViewById(R.id.ok_button);
+ mCancel = (Button)findViewById(R.id.cancel_button);
+ mOk.setOnClickListener(this);
+ mCancel.setOnClickListener(this);
+
+ mOk.setEnabled(false);
+
+ PackageUtil.initSnippetForNewApp(this, mAppSnippet, R.id.app_snippet);
+ }
+
+ /**
+ * Check if it is allowed to install the package and initiate install if allowed. If not allowed
+ * show the appropriate dialog.
+ */
+ private void checkIfAllowedAndInitiateInstall() {
+ // Check for install apps user restriction first.
+ final int installAppsRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_APPS, Process.myUserHandle());
+ if ((installAppsRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ showDialogInner(DLG_INSTALL_APPS_RESTRICTED_FOR_USER);
+ return;
+ } else if (installAppsRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
+ finish();
+ return;
+ }
+
+ if (mAllowUnknownSources || !isInstallRequestFromUnknownSource(getIntent())) {
+ initiateInstall();
+ } else {
+ // Check for unknown sources restriction
+ final int unknownSourcesRestrictionSource = mUserManager.getUserRestrictionSource(
+ UserManager.DISALLOW_INSTALL_UNKNOWN_SOURCES, Process.myUserHandle());
+ if ((unknownSourcesRestrictionSource & UserManager.RESTRICTION_SOURCE_SYSTEM) != 0) {
+ showDialogInner(DLG_UNKNOWN_SOURCES_RESTRICTED_FOR_USER);
+ } else if (unknownSourcesRestrictionSource != UserManager.RESTRICTION_NOT_SET) {
+ startActivity(new Intent(Settings.ACTION_SHOW_ADMIN_SUPPORT_DETAILS));
+ finish();
+ } else {
+ handleUnknownSources();
+ }
+ }
+ }
+
+ private void handleUnknownSources() {
+ if (mOriginatingPackage == null) {
+ Log.i(TAG, "No source found for package " + mPkgInfo.packageName);
+ showDialogInner(DLG_ANONYMOUS_SOURCE);
+ return;
+ }
+ // Shouldn't use static constant directly, see b/65534401.
+ final int appOpCode =
+ AppOpsManager.permissionToOpCode(Manifest.permission.REQUEST_INSTALL_PACKAGES);
+ final int appOpMode = mAppOpsManager.noteOpNoThrow(appOpCode,
+ mOriginatingUid, mOriginatingPackage);
+ switch (appOpMode) {
+ case AppOpsManager.MODE_DEFAULT:
+ try {
+ int result = mIpm.checkUidPermission(
+ Manifest.permission.REQUEST_INSTALL_PACKAGES, mOriginatingUid);
+ if (result == PackageManager.PERMISSION_GRANTED) {
+ initiateInstall();
+ break;
+ }
+ } catch (RemoteException exc) {
+ Log.e(TAG, "Unable to talk to package manager");
+ }
+ mAppOpsManager.setMode(appOpCode, mOriginatingUid,
+ mOriginatingPackage, AppOpsManager.MODE_ERRORED);
+ // fall through
+ case AppOpsManager.MODE_ERRORED:
+ showDialogInner(DLG_EXTERNAL_SOURCE_BLOCKED);
+ break;
+ case AppOpsManager.MODE_ALLOWED:
+ initiateInstall();
+ break;
+ default:
+ Log.e(TAG, "Invalid app op mode " + appOpMode
+ + " for OP_REQUEST_INSTALL_PACKAGES found for uid " + mOriginatingUid);
+ finish();
+ break;
+ }
+ }
+
+ /**
+ * Parse the Uri and set up the installer for this package.
+ *
+ * @param packageUri The URI to parse
+ *
+ * @return {@code true} iff the installer could be set up
+ */
+ private boolean processPackageUri(final Uri packageUri) {
+ mPackageURI = packageUri;
+
+ final String scheme = packageUri.getScheme();
+
+ switch (scheme) {
+ case SCHEME_PACKAGE: {
+ try {
+ mPkgInfo = mPm.getPackageInfo(packageUri.getSchemeSpecificPart(),
+ PackageManager.GET_PERMISSIONS
+ | PackageManager.MATCH_UNINSTALLED_PACKAGES);
+ } catch (NameNotFoundException e) {
+ }
+ if (mPkgInfo == null) {
+ Log.w(TAG, "Requested package " + packageUri.getScheme()
+ + " not available. Discontinuing installation");
+ showDialogInner(DLG_PACKAGE_ERROR);
+ setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
+ return false;
+ }
+ mAppSnippet = new PackageUtil.AppSnippet(mPm.getApplicationLabel(mPkgInfo.applicationInfo),
+ mPm.getApplicationIcon(mPkgInfo.applicationInfo));
+ } break;
+
+ case ContentResolver.SCHEME_FILE: {
+ File sourceFile = new File(packageUri.getPath());
+ PackageParser.Package parsed = PackageUtil.getPackageInfo(this, sourceFile);
+
+ // Check for parse errors
+ if (parsed == null) {
+ Log.w(TAG, "Parse error when parsing manifest. Discontinuing installation");
+ showDialogInner(DLG_PACKAGE_ERROR);
+ setPmResult(PackageManager.INSTALL_FAILED_INVALID_APK);
+ return false;
+ }
+ mPkgInfo = PackageParser.generatePackageInfo(parsed, null,
+ PackageManager.GET_PERMISSIONS, 0, 0, null,
+ new PackageUserState());
+ mAppSnippet = PackageUtil.getAppSnippet(this, mPkgInfo.applicationInfo, sourceFile);
+ } break;
+
+ default: {
+ throw new IllegalArgumentException("Unexpected URI scheme " + packageUri);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mSessionId != -1) {
+ mInstaller.setPermissionsResult(mSessionId, false);
+ }
+ super.onBackPressed();
+ }
+
+ public void onClick(View v) {
+ if (v == mOk) {
+ if (mOk.isEnabled()) {
+ if (mSessionId != -1) {
+ mInstaller.setPermissionsResult(mSessionId, true);
+ finish();
+ } else {
+ startInstall();
+ }
+ }
+ } else if (v == mCancel) {
+ // Cancel and finish
+ setResult(RESULT_CANCELED);
+ if (mSessionId != -1) {
+ mInstaller.setPermissionsResult(mSessionId, false);
+ }
+ finish();
+ }
+ }
+
+ private void startInstall() {
+ // Start subactivity to actually install the application
+ Intent newIntent = new Intent();
+ newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO,
+ mPkgInfo.applicationInfo);
+ newIntent.setData(mPackageURI);
+ newIntent.setClass(this, InstallInstalling.class);
+ String installerPackageName = getIntent().getStringExtra(
+ Intent.EXTRA_INSTALLER_PACKAGE_NAME);
+ if (mOriginatingURI != null) {
+ newIntent.putExtra(Intent.EXTRA_ORIGINATING_URI, mOriginatingURI);
+ }
+ if (mReferrerURI != null) {
+ newIntent.putExtra(Intent.EXTRA_REFERRER, mReferrerURI);
+ }
+ if (mOriginatingUid != PackageInstaller.SessionParams.UID_UNKNOWN) {
+ newIntent.putExtra(Intent.EXTRA_ORIGINATING_UID, mOriginatingUid);
+ }
+ if (installerPackageName != null) {
+ newIntent.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME,
+ installerPackageName);
+ }
+ if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ }
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ if(localLOGV) Log.i(TAG, "downloaded app uri="+mPackageURI);
+ startActivity(newIntent);
+ finish();
+ }
+
+ /**
+ * A simple error dialog showing a message
+ */
+ public static class SimpleErrorDialog extends DialogFragment {
+ private static final String MESSAGE_KEY =
+ SimpleErrorDialog.class.getName() + "MESSAGE_KEY";
+
+ static SimpleErrorDialog newInstance(@StringRes int message) {
+ SimpleErrorDialog dialog = new SimpleErrorDialog();
+
+ Bundle args = new Bundle();
+ args.putInt(MESSAGE_KEY, message);
+ dialog.setArguments(args);
+
+ return dialog;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(getArguments().getInt(MESSAGE_KEY))
+ .setPositiveButton(R.string.ok, (dialog, which) -> getActivity().finish())
+ .create();
+ }
+ }
+
+ /**
+ * Dialog to show when the source of apk can not be identified
+ */
+ public static class AnonymousSourceDialog extends DialogFragment {
+ static AnonymousSourceDialog newInstance() {
+ return new AnonymousSourceDialog();
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(R.string.anonymous_source_warning)
+ .setPositiveButton(R.string.anonymous_source_continue,
+ ((dialog, which) -> {
+ PackageInstallerActivity activity = ((PackageInstallerActivity)
+ getActivity());
+
+ activity.mAllowUnknownSources = true;
+ activity.initiateInstall();
+ }))
+ .setNegativeButton(R.string.cancel, ((dialog, which) -> getActivity().finish()))
+ .create();
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().finish();
+ }
+ }
+
+ /**
+ * An error dialog shown when the app is not supported on wear
+ */
+ public static class NotSupportedOnWearDialog extends SimpleErrorDialog {
+ static SimpleErrorDialog newInstance() {
+ return SimpleErrorDialog.newInstance(R.string.wear_not_allowed_dlg_text);
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().setResult(RESULT_OK);
+ getActivity().finish();
+ }
+ }
+
+ /**
+ * An error dialog shown when the device is out of space
+ */
+ public static class OutOfSpaceDialog extends AppErrorDialog {
+ static AppErrorDialog newInstance(@NonNull CharSequence applicationLabel) {
+ OutOfSpaceDialog dialog = new OutOfSpaceDialog();
+ dialog.setArgument(applicationLabel);
+ return dialog;
+ }
+
+ @Override
+ protected Dialog createDialog(@NonNull CharSequence argument) {
+ String dlgText = getString(R.string.out_of_space_dlg_text, argument);
+ return new AlertDialog.Builder(getActivity())
+ .setMessage(dlgText)
+ .setPositiveButton(R.string.manage_applications, (dialog, which) -> {
+ // launch manage applications
+ Intent intent = new Intent("android.intent.action.MANAGE_PACKAGE_STORAGE");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ getActivity().finish();
+ })
+ .setNegativeButton(R.string.cancel, (dialog, which) -> getActivity().finish())
+ .create();
+ }
+ }
+
+ /**
+ * A generic install-error dialog
+ */
+ public static class InstallErrorDialog extends AppErrorDialog {
+ static AppErrorDialog newInstance(@NonNull CharSequence applicationLabel) {
+ InstallErrorDialog dialog = new InstallErrorDialog();
+ dialog.setArgument(applicationLabel);
+ return dialog;
+ }
+
+ @Override
+ protected Dialog createDialog(@NonNull CharSequence argument) {
+ return new AlertDialog.Builder(getActivity())
+ .setNeutralButton(R.string.ok, (dialog, which) -> getActivity().finish())
+ .setMessage(getString(R.string.install_failed_msg, argument))
+ .create();
+ }
+ }
+
+ /**
+ * An error dialog shown when external sources are not allowed
+ */
+ public static class ExternalSourcesBlockedDialog extends AppErrorDialog {
+ static AppErrorDialog newInstance(@NonNull String originationPkg) {
+ ExternalSourcesBlockedDialog dialog = new ExternalSourcesBlockedDialog();
+ dialog.setArgument(originationPkg);
+ return dialog;
+ }
+
+ @Override
+ protected Dialog createDialog(@NonNull CharSequence argument) {
+ try {
+ PackageManager pm = getActivity().getPackageManager();
+
+ ApplicationInfo sourceInfo = pm.getApplicationInfo(argument.toString(), 0);
+
+ return new AlertDialog.Builder(getActivity())
+ .setTitle(pm.getApplicationLabel(sourceInfo))
+ .setIcon(pm.getApplicationIcon(sourceInfo))
+ .setMessage(R.string.untrusted_external_source_warning)
+ .setPositiveButton(R.string.external_sources_settings,
+ (dialog, which) -> {
+ Intent settingsIntent = new Intent();
+ settingsIntent.setAction(
+ Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ final Uri packageUri = Uri.parse("package:" + argument);
+ settingsIntent.setData(packageUri);
+ try {
+ getActivity().startActivityForResult(settingsIntent,
+ REQUEST_TRUST_EXTERNAL_SOURCE);
+ } catch (ActivityNotFoundException exc) {
+ Log.e(TAG, "Settings activity not found for action: "
+ + Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES);
+ }
+ })
+ .setNegativeButton(R.string.cancel,
+ (dialog, which) -> getActivity().finish())
+ .create();
+ } catch (NameNotFoundException e) {
+ Log.e(TAG, "Did not find app info for " + argument);
+ getActivity().finish();
+ return null;
+ }
+ }
+ }
+
+ /**
+ * Superclass for all error dialogs. Stores a single CharSequence argument
+ */
+ public abstract static class AppErrorDialog extends DialogFragment {
+ private static final String ARGUMENT_KEY = AppErrorDialog.class.getName() + "ARGUMENT_KEY";
+
+ protected void setArgument(@NonNull CharSequence argument) {
+ Bundle args = new Bundle();
+ args.putCharSequence(ARGUMENT_KEY, argument);
+ setArguments(args);
+ }
+
+ protected abstract Dialog createDialog(@NonNull CharSequence argument);
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ return createDialog(getArguments().getString(ARGUMENT_KEY));
+ }
+
+ @Override
+ public void onCancel(DialogInterface dialog) {
+ getActivity().finish();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java
new file mode 100644
index 0000000000000..9b7e64e81aa38
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageInstallerApplication.java
@@ -0,0 +1,28 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Application;
+import android.content.pm.PackageItemInfo;
+
+public class PackageInstallerApplication extends Application {
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ PackageItemInfo.setForceSafeLabels(true);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
new file mode 100644
index 0000000000000..68daed7602ace
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/PackageUtil.java
@@ -0,0 +1,210 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.packageinstaller;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.content.pm.PackageParser.PackageParserException;
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.UserHandle;
+import androidx.annotation.NonNull;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import java.io.File;
+
+/**
+ * This is a utility class for defining some utility methods and constants
+ * used in the package installer application.
+ */
+public class PackageUtil {
+ private static final String LOG_TAG = PackageUtil.class.getSimpleName();
+
+ public static final String PREFIX="com.android.packageinstaller.";
+ public static final String INTENT_ATTR_INSTALL_STATUS = PREFIX+"installStatus";
+ public static final String INTENT_ATTR_APPLICATION_INFO=PREFIX+"applicationInfo";
+ public static final String INTENT_ATTR_PERMISSIONS_LIST=PREFIX+"PermissionsList";
+ //intent attribute strings related to uninstall
+ public static final String INTENT_ATTR_PACKAGE_NAME=PREFIX+"PackageName";
+
+ /**
+ * Utility method to get package information for a given {@link File}
+ */
+ public static PackageParser.Package getPackageInfo(Context context, File sourceFile) {
+ final PackageParser parser = new PackageParser();
+ parser.setCallback(new PackageParser.CallbackImpl(context.getPackageManager()));
+ try {
+ return parser.parsePackage(sourceFile, 0);
+ } catch (PackageParserException e) {
+ return null;
+ }
+ }
+
+ public static View initSnippet(View snippetView, CharSequence label, Drawable icon) {
+ ((ImageView)snippetView.findViewById(R.id.app_icon)).setImageDrawable(icon);
+ ((TextView)snippetView.findViewById(R.id.app_name)).setText(label);
+ return snippetView;
+ }
+
+ /**
+ * Utility method to display a snippet of an installed application.
+ * The content view should have been set on context before invoking this method.
+ * appSnippet view should include R.id.app_icon and R.id.app_name
+ * defined on it.
+ *
+ * @param pContext context of package that can load the resources
+ * @param componentInfo ComponentInfo object whose resources are to be loaded
+ * @param snippetView the snippet view
+ */
+ public static View initSnippetForInstalledApp(Context pContext,
+ ApplicationInfo appInfo, View snippetView) {
+ return initSnippetForInstalledApp(pContext, appInfo, snippetView, null);
+ }
+
+ /**
+ * Utility method to display a snippet of an installed application.
+ * The content view should have been set on context before invoking this method.
+ * appSnippet view should include R.id.app_icon and R.id.app_name
+ * defined on it.
+ *
+ * @param pContext context of package that can load the resources
+ * @param componentInfo ComponentInfo object whose resources are to be loaded
+ * @param snippetView the snippet view
+ * @param UserHandle user that the app si installed for.
+ */
+ public static View initSnippetForInstalledApp(Context pContext,
+ ApplicationInfo appInfo, View snippetView, UserHandle user) {
+ final PackageManager pm = pContext.getPackageManager();
+ Drawable icon = appInfo.loadIcon(pm);
+ if (user != null) {
+ icon = pContext.getPackageManager().getUserBadgedIcon(icon, user);
+ }
+ return initSnippet(
+ snippetView,
+ appInfo.loadLabel(pm),
+ icon);
+ }
+
+ /**
+ * Utility method to display application snippet of a new package.
+ * The content view should have been set on context before invoking this method.
+ * appSnippet view should include R.id.app_icon and R.id.app_name
+ * defined on it.
+ *
+ * @param pContext context of package that can load the resources
+ * @param as The resources to be loaded
+ * @param snippetId view id of app snippet view
+ */
+ @NonNull public static View initSnippetForNewApp(@NonNull Activity pContext,
+ @NonNull AppSnippet as, int snippetId) {
+ View appSnippet = pContext.findViewById(snippetId);
+ if (as.icon != null) {
+ ((ImageView) appSnippet.findViewById(R.id.app_icon)).setImageDrawable(as.icon);
+ }
+ ((TextView)appSnippet.findViewById(R.id.app_name)).setText(as.label);
+ return appSnippet;
+ }
+
+ static public class AppSnippet {
+ @NonNull public CharSequence label;
+ @Nullable public Drawable icon;
+ public AppSnippet(@NonNull CharSequence label, @Nullable Drawable icon) {
+ this.label = label;
+ this.icon = icon;
+ }
+ }
+
+ /**
+ * Utility method to load application label
+ *
+ * @param pContext context of package that can load the resources
+ * @param appInfo ApplicationInfo object of package whose resources are to be loaded
+ * @param sourceFile File the package is in
+ */
+ public static AppSnippet getAppSnippet(
+ Activity pContext, ApplicationInfo appInfo, File sourceFile) {
+ final String archiveFilePath = sourceFile.getAbsolutePath();
+ Resources pRes = pContext.getResources();
+ AssetManager assmgr = new AssetManager();
+ assmgr.addAssetPath(archiveFilePath);
+ Resources res = new Resources(assmgr, pRes.getDisplayMetrics(), pRes.getConfiguration());
+ CharSequence label = null;
+ // Try to load the label from the package's resources. If an app has not explicitly
+ // specified any label, just use the package name.
+ if (appInfo.labelRes != 0) {
+ try {
+ label = res.getText(appInfo.labelRes);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ if (label == null) {
+ label = (appInfo.nonLocalizedLabel != null) ?
+ appInfo.nonLocalizedLabel : appInfo.packageName;
+ }
+ Drawable icon = null;
+ // Try to load the icon from the package's resources. If an app has not explicitly
+ // specified any resource, just use the default icon for now.
+ try {
+ if (appInfo.icon != 0) {
+ try {
+ icon = res.getDrawable(appInfo.icon);
+ } catch (Resources.NotFoundException e) {
+ }
+ }
+ if (icon == null) {
+ icon = pContext.getPackageManager().getDefaultActivityIcon();
+ }
+ } catch (OutOfMemoryError e) {
+ Log.i(LOG_TAG, "Could not load app icon", e);
+ }
+ return new PackageUtil.AppSnippet(label, icon);
+ }
+
+ /**
+ * Get the maximum target sdk for a UID.
+ *
+ * @param context The context to use
+ * @param uid The UID requesting the install/uninstall
+ *
+ * @return The maximum target SDK or -1 if the uid does not match any packages.
+ */
+ static int getMaxTargetSdkVersionForUid(@NonNull Context context, int uid) {
+ PackageManager pm = context.getPackageManager();
+ final String[] packages = pm.getPackagesForUid(uid);
+ int targetSdkVersion = -1;
+ if (packages != null) {
+ for (String packageName : packages) {
+ try {
+ ApplicationInfo info = pm.getApplicationInfo(packageName, 0);
+ targetSdkVersion = Math.max(targetSdkVersion, info.targetSdkVersion);
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore and try the next package
+ }
+ }
+ }
+ return targetSdkVersion;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java
new file mode 100644
index 0000000000000..7d8064dd789cf
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/RemoveReceiver.java
@@ -0,0 +1,42 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.SharedPreferences;
+import android.net.Uri;
+
+public class RemoveReceiver extends BroadcastReceiver {
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ if (Intent.ACTION_PACKAGE_FULLY_REMOVED.equals(intent.getAction())) {
+ Uri uri = intent.getData();
+ String pkg = uri != null ? uri.getSchemeSpecificPart() : null;
+ if (pkg != null) {
+ SharedPreferences prefs = context.getSharedPreferences(
+ PackageInstallerActivity.PREFS_ALLOWED_SOURCES,
+ Context.MODE_PRIVATE);
+ if (prefs.getBoolean(pkg, false)) {
+ prefs.edit().remove(pkg).apply();
+ }
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
new file mode 100644
index 0000000000000..66e93b3100ecc
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/TemporaryFileManager.java
@@ -0,0 +1,95 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.SystemClock;
+import androidx.annotation.NonNull;
+import android.util.Log;
+
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * Manages files of the package installer and resets state during boot.
+ */
+public class TemporaryFileManager extends BroadcastReceiver {
+ private static final String LOG_TAG = TemporaryFileManager.class.getSimpleName();
+
+ /**
+ * Create a new file to hold a staged file.
+ *
+ * @param context The context of the caller
+ *
+ * @return A new file
+ */
+ @NonNull
+ public static File getStagedFile(@NonNull Context context) throws IOException {
+ return File.createTempFile("package", ".apk", context.getNoBackupFilesDir());
+ }
+
+ /**
+ * Get the file used to store the results of installs.
+ *
+ * @param context The context of the caller
+ *
+ * @return the file used to store the results of installs
+ */
+ @NonNull
+ public static File getInstallStateFile(@NonNull Context context) {
+ return new File(context.getNoBackupFilesDir(), "install_results.xml");
+ }
+
+ /**
+ * Get the file used to store the results of uninstalls.
+ *
+ * @param context The context of the caller
+ *
+ * @return the file used to store the results of uninstalls
+ */
+ @NonNull
+ public static File getUninstallStateFile(@NonNull Context context) {
+ return new File(context.getNoBackupFilesDir(), "uninstall_results.xml");
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ long systemBootTime = System.currentTimeMillis() - SystemClock.elapsedRealtime();
+
+ File[] filesOnBoot = context.getNoBackupFilesDir().listFiles();
+
+ if (filesOnBoot == null) {
+ return;
+ }
+
+ for (int i = 0; i < filesOnBoot.length; i++) {
+ File fileOnBoot = filesOnBoot[i];
+
+ if (systemBootTime > fileOnBoot.lastModified()) {
+ boolean wasDeleted = fileOnBoot.delete();
+ if (!wasDeleted) {
+ Log.w(LOG_TAG, "Could not delete " + fileOnBoot.getName() + " onBoot");
+ }
+ } else {
+ Log.w(LOG_TAG, fileOnBoot.getName() + " was created before onBoot broadcast was "
+ + "received");
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
new file mode 100644
index 0000000000000..ad3cd4c65aac2
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallEventReceiver.java
@@ -0,0 +1,85 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import androidx.annotation.NonNull;
+
+/**
+ * Receives uninstall events and persists them using a {@link EventResultPersister}.
+ */
+public class UninstallEventReceiver extends BroadcastReceiver {
+ private static final Object sLock = new Object();
+ private static EventResultPersister sReceiver;
+
+ /**
+ * Get the event receiver persisting the results
+ *
+ * @return The event receiver.
+ */
+ @NonNull private static EventResultPersister getReceiver(@NonNull Context context) {
+ synchronized (sLock) {
+ if (sReceiver == null) {
+ sReceiver = new EventResultPersister(
+ TemporaryFileManager.getUninstallStateFile(context));
+ }
+ }
+
+ return sReceiver;
+ }
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ getReceiver(context).onEventReceived(context, intent);
+ }
+
+ /**
+ * Add an observer. If there is already an event for this id, call back inside of this call.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer is for or {@code GENERATE_NEW_ID} to generate a new one.
+ * @param observer The observer to call back.
+ *
+ * @return The id for this event
+ */
+ static int addObserver(@NonNull Context context, int id,
+ @NonNull EventResultPersister.EventResultObserver observer)
+ throws EventResultPersister.OutOfIdsException {
+ return getReceiver(context).addObserver(id, observer);
+ }
+
+ /**
+ * Remove a observer.
+ *
+ * @param context A context of the current app
+ * @param id The id the observer was added for
+ */
+ static void removeObserver(@NonNull Context context, int id) {
+ getReceiver(context).removeObserver(id);
+ }
+
+ /**
+ * @param context A context of the current app
+ *
+ * @return A new uninstall id
+ */
+ static int getNewId(@NonNull Context context) throws EventResultPersister.OutOfIdsException {
+ return getReceiver(context).getNewId();
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
new file mode 100644
index 0000000000000..8282c83822923
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallFinish.java
@@ -0,0 +1,265 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.app.admin.IDevicePolicyManager;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.graphics.drawable.Icon;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.provider.Settings;
+import androidx.annotation.NonNull;
+import android.util.Log;
+import android.widget.Toast;
+
+import java.util.List;
+
+/**
+ * Finish an uninstallation and show Toast on success or failure notification.
+ */
+public class UninstallFinish extends BroadcastReceiver {
+ private static final String LOG_TAG = UninstallFinish.class.getSimpleName();
+
+ private static final String UNINSTALL_FAILURE_CHANNEL = "uninstall failure";
+
+ static final String EXTRA_UNINSTALL_ID = "com.android.packageinstaller.extra.UNINSTALL_ID";
+ static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ int returnCode = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, 0);
+
+ Log.i(LOG_TAG, "Uninstall finished extras=" + intent.getExtras());
+
+ if (returnCode == PackageInstaller.STATUS_PENDING_USER_ACTION) {
+ context.startActivity(intent.getParcelableExtra(Intent.EXTRA_INTENT));
+ return;
+ }
+
+ int uninstallId = intent.getIntExtra(EXTRA_UNINSTALL_ID, 0);
+ ApplicationInfo appInfo = intent.getParcelableExtra(
+ PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ String appLabel = intent.getStringExtra(EXTRA_APP_LABEL);
+ boolean allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+
+ NotificationManager notificationManager =
+ context.getSystemService(NotificationManager.class);
+ UserManager userManager = context.getSystemService(UserManager.class);
+
+ NotificationChannel uninstallFailureChannel = new NotificationChannel(
+ UNINSTALL_FAILURE_CHANNEL,
+ context.getString(R.string.uninstall_failure_notification_channel),
+ NotificationManager.IMPORTANCE_DEFAULT);
+ notificationManager.createNotificationChannel(uninstallFailureChannel);
+
+ Notification.Builder uninstallFailedNotification = new Notification.Builder(context,
+ UNINSTALL_FAILURE_CHANNEL);
+
+ switch (returnCode) {
+ case PackageInstaller.STATUS_SUCCESS:
+ notificationManager.cancel(uninstallId);
+
+ Toast.makeText(context, context.getString(R.string.uninstall_done_app, appLabel),
+ Toast.LENGTH_LONG).show();
+ return;
+ case PackageInstaller.STATUS_FAILURE_BLOCKED: {
+ int legacyStatus = intent.getIntExtra(PackageInstaller.EXTRA_LEGACY_STATUS, 0);
+
+ switch (legacyStatus) {
+ case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
+ IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ // Find out if the package is an active admin for some non-current user.
+ int myUserId = UserHandle.myUserId();
+ UserInfo otherBlockingUser = null;
+ for (UserInfo user : userManager.getUsers()) {
+ // We only catch the case when the user in question is neither the
+ // current user nor its profile.
+ if (isProfileOfOrSame(userManager, myUserId, user.id)) {
+ continue;
+ }
+
+ try {
+ if (dpm.packageHasActiveAdmins(appInfo.packageName, user.id)) {
+ otherBlockingUser = user;
+ break;
+ }
+ } catch (RemoteException e) {
+ Log.e(LOG_TAG, "Failed to talk to package manager", e);
+ }
+ }
+ if (otherBlockingUser == null) {
+ Log.d(LOG_TAG, "Uninstall failed because " + appInfo.packageName
+ + " is a device admin");
+
+ addDeviceManagerButton(context, uninstallFailedNotification);
+ setBigText(uninstallFailedNotification, context.getString(
+ R.string.uninstall_failed_device_policy_manager));
+ } else {
+ Log.d(LOG_TAG, "Uninstall failed because " + appInfo.packageName
+ + " is a device admin of user " + otherBlockingUser);
+
+ setBigText(uninstallFailedNotification, String.format(context.getString(
+ R.string.uninstall_failed_device_policy_manager_of_user),
+ otherBlockingUser.name));
+ }
+ break;
+ }
+ case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
+ IPackageManager packageManager = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+
+ List users = userManager.getUsers();
+ int blockingUserId = UserHandle.USER_NULL;
+ for (int i = 0; i < users.size(); ++i) {
+ final UserInfo user = users.get(i);
+ try {
+ if (packageManager.getBlockUninstallForUser(appInfo.packageName,
+ user.id)) {
+ blockingUserId = user.id;
+ break;
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Log.e(LOG_TAG, "Failed to talk to package manager", e);
+ }
+ }
+
+ int myUserId = UserHandle.myUserId();
+ if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
+ addDeviceManagerButton(context, uninstallFailedNotification);
+ } else {
+ addManageUsersButton(context, uninstallFailedNotification);
+ }
+
+ if (blockingUserId == UserHandle.USER_NULL) {
+ Log.d(LOG_TAG,
+ "Uninstall failed for " + appInfo.packageName + " with code "
+ + returnCode + " no blocking user");
+ } else if (blockingUserId == UserHandle.USER_SYSTEM) {
+ setBigText(uninstallFailedNotification,
+ context.getString(R.string.uninstall_blocked_device_owner));
+ } else {
+ if (allUsers) {
+ setBigText(uninstallFailedNotification,
+ context.getString(
+ R.string.uninstall_all_blocked_profile_owner));
+ } else {
+ setBigText(uninstallFailedNotification, context.getString(
+ R.string.uninstall_blocked_profile_owner));
+ }
+ }
+ break;
+ }
+ default:
+ Log.d(LOG_TAG, "Uninstall blocked for " + appInfo.packageName
+ + " with legacy code " + legacyStatus);
+ } break;
+ }
+ default:
+ Log.d(LOG_TAG, "Uninstall failed for " + appInfo.packageName + " with code "
+ + returnCode);
+ break;
+ }
+
+ uninstallFailedNotification.setContentTitle(
+ context.getString(R.string.uninstall_failed_app, appLabel));
+ uninstallFailedNotification.setOngoing(false);
+ uninstallFailedNotification.setSmallIcon(R.drawable.ic_error);
+ notificationManager.notify(uninstallId, uninstallFailedNotification.build());
+ }
+
+ /**
+ * Is a profile part of a user?
+ *
+ * @param userManager The user manager
+ * @param userId The id of the user
+ * @param profileId The id of the profile
+ *
+ * @return If the profile is part of the user or the profile parent of the user
+ */
+ private boolean isProfileOfOrSame(@NonNull UserManager userManager, int userId, int profileId) {
+ if (userId == profileId) {
+ return true;
+ }
+
+ UserInfo parentUser = userManager.getProfileParent(profileId);
+ return parentUser != null && parentUser.id == userId;
+ }
+
+ /**
+ * Set big text for the notification.
+ *
+ * @param builder The builder of the notification
+ * @param text The text to set.
+ */
+ private void setBigText(@NonNull Notification.Builder builder,
+ @NonNull CharSequence text) {
+ builder.setStyle(new Notification.BigTextStyle().bigText(text));
+ }
+
+ /**
+ * Add a button to the notification that links to the user management.
+ *
+ * @param context The context the notification is created in
+ * @param builder The builder of the notification
+ */
+ private void addManageUsersButton(@NonNull Context context,
+ @NonNull Notification.Builder builder) {
+ Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ builder.addAction((new Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_settings_multiuser),
+ context.getString(R.string.manage_users),
+ PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ }
+
+ /**
+ * Add a button to the notification that links to the device policy management.
+ *
+ * @param context The context the notification is created in
+ * @param builder The builder of the notification
+ */
+ private void addDeviceManagerButton(@NonNull Context context,
+ @NonNull Notification.Builder builder) {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.Settings$DeviceAdminSettingsActivity");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+
+ builder.addAction((new Notification.Action.Builder(
+ Icon.createWithResource(context, R.drawable.ic_lock),
+ context.getString(R.string.manage_device_administrators),
+ PendingIntent.getActivity(context, 0, intent,
+ PendingIntent.FLAG_UPDATE_CURRENT))).build());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
new file mode 100644
index 0000000000000..e27186ba5e615
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallUninstalling.java
@@ -0,0 +1,183 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller;
+
+import android.app.Activity;
+import android.app.ActivityThread;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.PendingIntent;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.UserHandle;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.widget.Toast;
+
+/**
+ * Start an uninstallation, show a dialog while uninstalling and return result to the caller.
+ */
+public class UninstallUninstalling extends Activity implements
+ EventResultPersister.EventResultObserver {
+ private static final String LOG_TAG = UninstallUninstalling.class.getSimpleName();
+
+ private static final String UNINSTALL_ID = "com.android.packageinstaller.UNINSTALL_ID";
+ private static final String BROADCAST_ACTION =
+ "com.android.packageinstaller.ACTION_UNINSTALL_COMMIT";
+
+ static final String EXTRA_APP_LABEL = "com.android.packageinstaller.extra.APP_LABEL";
+
+ private int mUninstallId;
+ private ApplicationInfo mAppInfo;
+ private IBinder mCallback;
+ private boolean mReturnResult;
+ private String mLabel;
+
+ @Override
+ protected void onCreate(@Nullable Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ setFinishOnTouchOutside(false);
+
+ mAppInfo = getIntent().getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ mCallback = getIntent().getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+ mReturnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+ mLabel = getIntent().getStringExtra(EXTRA_APP_LABEL);
+
+ try {
+ if (savedInstanceState == null) {
+ boolean allUsers = getIntent().getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS,
+ false);
+ UserHandle user = getIntent().getParcelableExtra(Intent.EXTRA_USER);
+
+ // Show dialog, which is the whole UI
+ FragmentTransaction transaction = getFragmentManager().beginTransaction();
+ Fragment prev = getFragmentManager().findFragmentByTag("dialog");
+ if (prev != null) {
+ transaction.remove(prev);
+ }
+ DialogFragment dialog = new UninstallUninstallingFragment();
+ dialog.setCancelable(false);
+ dialog.show(transaction, "dialog");
+
+ mUninstallId = UninstallEventReceiver.addObserver(this,
+ EventResultPersister.GENERATE_NEW_ID, this);
+
+ Intent broadcastIntent = new Intent(BROADCAST_ACTION);
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ broadcastIntent.putExtra(EventResultPersister.EXTRA_ID, mUninstallId);
+ broadcastIntent.setPackage(getPackageName());
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, mUninstallId,
+ broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ try {
+ ActivityThread.getPackageManager().getPackageInstaller().uninstall(
+ new VersionedPackage(mAppInfo.packageName,
+ PackageManager.VERSION_CODE_HIGHEST),
+ getPackageName(), allUsers ? PackageManager.DELETE_ALL_USERS : 0,
+ pendingIntent.getIntentSender(), user.getIdentifier());
+ } catch (RemoteException e) {
+ e.rethrowFromSystemServer();
+ }
+ } else {
+ mUninstallId = savedInstanceState.getInt(UNINSTALL_ID);
+ UninstallEventReceiver.addObserver(this, mUninstallId, this);
+ }
+ } catch (EventResultPersister.OutOfIdsException | IllegalArgumentException e) {
+ Log.e(LOG_TAG, "Fails to start uninstall", e);
+ onResult(PackageInstaller.STATUS_FAILURE, PackageManager.DELETE_FAILED_INTERNAL_ERROR,
+ null);
+ }
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putInt(UNINSTALL_ID, mUninstallId);
+ }
+
+ @Override
+ public void onBackPressed() {
+ // do nothing
+ }
+
+ @Override
+ public void onResult(int status, int legacyStatus, @Nullable String message) {
+ if (mCallback != null) {
+ // The caller will be informed about the result via a callback
+ final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+ .asInterface(mCallback);
+ try {
+ observer.onPackageDeleted(mAppInfo.packageName, legacyStatus, message);
+ } catch (RemoteException ignored) {
+ }
+ } else if (mReturnResult) {
+ // The caller will be informed about the result and might decide to display it
+ Intent result = new Intent();
+
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT, legacyStatus);
+ setResult(status == PackageInstaller.STATUS_SUCCESS ? Activity.RESULT_OK
+ : Activity.RESULT_FIRST_USER, result);
+ } else {
+ // This is the rare case that the caller did not ask for the result, but wanted to be
+ // notified via onActivityResult when the installation finishes
+ if (status != PackageInstaller.STATUS_SUCCESS) {
+ Toast.makeText(this, getString(R.string.uninstall_failed_app, mLabel),
+ Toast.LENGTH_LONG).show();
+ }
+ }
+ finish();
+ }
+
+ @Override
+ protected void onDestroy() {
+ UninstallEventReceiver.removeObserver(this, mUninstallId);
+
+ super.onDestroy();
+ }
+
+ /**
+ * Dialog that shows that the app is uninstalling.
+ */
+ public static class UninstallUninstallingFragment extends DialogFragment {
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+
+ dialogBuilder.setCancelable(false);
+ dialogBuilder.setMessage(getActivity().getString(R.string.uninstalling_app,
+ ((UninstallUninstalling) getActivity()).mLabel));
+
+ Dialog dialog = dialogBuilder.create();
+ dialog.setCanceledOnTouchOutside(false);
+
+ return dialog;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
new file mode 100755
index 0000000000000..d5df9f52d5fb6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/UninstallerActivity.java
@@ -0,0 +1,395 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.packageinstaller;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+
+import static com.android.packageinstaller.PackageUtil.getMaxTargetSdkVersionForUid;
+
+import android.Manifest;
+import android.app.Activity;
+import android.app.ActivityManager;
+import android.app.ActivityThread;
+import android.app.AppGlobals;
+import android.app.AppOpsManager;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.app.FragmentTransaction;
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.VersionedPackage;
+import android.content.res.Configuration;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import androidx.annotation.NonNull;
+import androidx.annotation.StringRes;
+import android.util.Log;
+
+import com.android.packageinstaller.handheld.ErrorDialogFragment;
+import com.android.packageinstaller.handheld.UninstallAlertDialogFragment;
+import com.android.packageinstaller.television.ErrorFragment;
+import com.android.packageinstaller.television.UninstallAlertFragment;
+import com.android.packageinstaller.television.UninstallAppProgress;
+
+import java.util.List;
+
+/*
+ * This activity presents UI to uninstall an application. Usually launched with intent
+ * Intent.ACTION_UNINSTALL_PKG_COMMAND and attribute
+ * com.android.packageinstaller.PackageName set to the application package name
+ */
+public class UninstallerActivity extends Activity {
+ private static final String TAG = "UninstallerActivity";
+
+ private static final String UNINSTALLING_CHANNEL = "uninstalling";
+
+ public static class DialogInfo {
+ public ApplicationInfo appInfo;
+ public ActivityInfo activityInfo;
+ public boolean allUsers;
+ public UserHandle user;
+ public IBinder callback;
+ }
+
+ private String mPackageName;
+ private DialogInfo mDialogInfo;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ // Never restore any state, esp. never create any fragments. The data in the fragment might
+ // be stale, if e.g. the app was uninstalled while the activity was destroyed.
+ super.onCreate(null);
+
+ try {
+ int callingUid = ActivityManager.getService().getLaunchedFromUid(getActivityToken());
+
+ String callingPackage = getPackageNameForUid(callingUid);
+ if (callingPackage == null) {
+ Log.e(TAG, "Package not found for originating uid " + callingUid);
+ setResult(Activity.RESULT_FIRST_USER);
+ finish();
+ return;
+ } else {
+ AppOpsManager appOpsManager = (AppOpsManager) getSystemService(
+ Context.APP_OPS_SERVICE);
+ if (appOpsManager.noteOpNoThrow(
+ AppOpsManager.OPSTR_REQUEST_DELETE_PACKAGES, callingUid, callingPackage)
+ != MODE_ALLOWED) {
+ Log.e(TAG, "Install from uid " + callingUid + " disallowed by AppOps");
+ setResult(Activity.RESULT_FIRST_USER);
+ finish();
+ return;
+ }
+ }
+
+ if (getMaxTargetSdkVersionForUid(this, callingUid)
+ >= Build.VERSION_CODES.P && AppGlobals.getPackageManager().checkUidPermission(
+ Manifest.permission.REQUEST_DELETE_PACKAGES, callingUid)
+ != PackageManager.PERMISSION_GRANTED
+ && AppGlobals.getPackageManager().checkUidPermission(
+ Manifest.permission.DELETE_PACKAGES, callingUid)
+ != PackageManager.PERMISSION_GRANTED) {
+ Log.e(TAG, "Uid " + callingUid + " does not have "
+ + Manifest.permission.REQUEST_DELETE_PACKAGES + " or "
+ + Manifest.permission.DELETE_PACKAGES);
+
+ setResult(Activity.RESULT_FIRST_USER);
+ finish();
+ return;
+ }
+ } catch (RemoteException ex) {
+ // Cannot reach Package/ActivityManager. Aborting uninstall.
+ Log.e(TAG, "Could not determine the launching uid.");
+
+ setResult(Activity.RESULT_FIRST_USER);
+ finish();
+ return;
+ }
+
+ // Get intent information.
+ // We expect an intent with URI of the form package://#
+ // className is optional; if specified, it is the activity the user chose to uninstall
+ final Intent intent = getIntent();
+ final Uri packageUri = intent.getData();
+ if (packageUri == null) {
+ Log.e(TAG, "No package URI in intent");
+ showAppNotFound();
+ return;
+ }
+ mPackageName = packageUri.getEncodedSchemeSpecificPart();
+ if (mPackageName == null) {
+ Log.e(TAG, "Invalid package name in URI: " + packageUri);
+ showAppNotFound();
+ return;
+ }
+
+ final IPackageManager pm = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+
+ mDialogInfo = new DialogInfo();
+
+ mDialogInfo.allUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+ if (mDialogInfo.allUsers && !UserManager.get(this).isAdminUser()) {
+ Log.e(TAG, "Only admin user can request uninstall for all users");
+ showUserIsNotAllowed();
+ return;
+ }
+ mDialogInfo.user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (mDialogInfo.user == null) {
+ mDialogInfo.user = android.os.Process.myUserHandle();
+ } else {
+ UserManager userManager = (UserManager) getSystemService(Context.USER_SERVICE);
+ List profiles = userManager.getUserProfiles();
+ if (!profiles.contains(mDialogInfo.user)) {
+ Log.e(TAG, "User " + android.os.Process.myUserHandle() + " can't request uninstall "
+ + "for user " + mDialogInfo.user);
+ showUserIsNotAllowed();
+ return;
+ }
+ }
+
+ mDialogInfo.callback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+
+ try {
+ mDialogInfo.appInfo = pm.getApplicationInfo(mPackageName,
+ PackageManager.MATCH_ANY_USER, mDialogInfo.user.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get packageName. Package manager is dead?");
+ }
+
+ if (mDialogInfo.appInfo == null) {
+ Log.e(TAG, "Invalid packageName: " + mPackageName);
+ showAppNotFound();
+ return;
+ }
+
+ // The class name may have been specified (e.g. when deleting an app from all apps)
+ final String className = packageUri.getFragment();
+ if (className != null) {
+ try {
+ mDialogInfo.activityInfo = pm.getActivityInfo(
+ new ComponentName(mPackageName, className), 0,
+ mDialogInfo.user.getIdentifier());
+ } catch (RemoteException e) {
+ Log.e(TAG, "Unable to get className. Package manager is dead?");
+ // Continue as the ActivityInfo isn't critical.
+ }
+ }
+
+ showConfirmationDialog();
+ }
+
+ public DialogInfo getDialogInfo() {
+ return mDialogInfo;
+ }
+
+ private void showConfirmationDialog() {
+ if (isTv()) {
+ showContentFragment(new UninstallAlertFragment(), 0, 0);
+ } else {
+ showDialogFragment(new UninstallAlertDialogFragment(), 0, 0);
+ }
+ }
+
+ private void showAppNotFound() {
+ if (isTv()) {
+ showContentFragment(new ErrorFragment(), R.string.app_not_found_dlg_title,
+ R.string.app_not_found_dlg_text);
+ } else {
+ showDialogFragment(new ErrorDialogFragment(), R.string.app_not_found_dlg_title,
+ R.string.app_not_found_dlg_text);
+ }
+ }
+
+ private void showUserIsNotAllowed() {
+ if (isTv()) {
+ showContentFragment(new ErrorFragment(),
+ R.string.user_is_not_allowed_dlg_title, R.string.user_is_not_allowed_dlg_text);
+ } else {
+ showDialogFragment(new ErrorDialogFragment(), 0, R.string.user_is_not_allowed_dlg_text);
+ }
+ }
+
+ private void showGenericError() {
+ if (isTv()) {
+ showContentFragment(new ErrorFragment(),
+ R.string.generic_error_dlg_title, R.string.generic_error_dlg_text);
+ } else {
+ showDialogFragment(new ErrorDialogFragment(), 0, R.string.generic_error_dlg_text);
+ }
+ }
+
+ private boolean isTv() {
+ return (getResources().getConfiguration().uiMode & Configuration.UI_MODE_TYPE_MASK)
+ == Configuration.UI_MODE_TYPE_TELEVISION;
+ }
+
+ private void showContentFragment(@NonNull Fragment fragment, @StringRes int title,
+ @StringRes int text) {
+ Bundle args = new Bundle();
+ args.putInt(ErrorFragment.TITLE, title);
+ args.putInt(ErrorFragment.TEXT, text);
+ fragment.setArguments(args);
+
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, fragment)
+ .commit();
+ }
+
+ private void showDialogFragment(@NonNull DialogFragment fragment,
+ @StringRes int title, @StringRes int text) {
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ Fragment prev = getFragmentManager().findFragmentByTag("dialog");
+ if (prev != null) {
+ ft.remove(prev);
+ }
+
+ Bundle args = new Bundle();
+ if (title != 0) {
+ args.putInt(ErrorDialogFragment.TITLE, title);
+ }
+ args.putInt(ErrorDialogFragment.TEXT, text);
+
+ fragment.setArguments(args);
+ fragment.show(ft, "dialog");
+ }
+
+ public void startUninstallProgress() {
+ boolean returnResult = getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false);
+ CharSequence label = mDialogInfo.appInfo.loadSafeLabel(getPackageManager());
+
+ if (isTv()) {
+ Intent newIntent = new Intent(Intent.ACTION_VIEW);
+ newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
+ newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+ newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
+ newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+
+ if (returnResult) {
+ newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ }
+
+ newIntent.setClass(this, UninstallAppProgress.class);
+ startActivity(newIntent);
+ } else if (returnResult || mDialogInfo.callback != null || getCallingActivity() != null) {
+ Intent newIntent = new Intent(this, UninstallUninstalling.class);
+
+ newIntent.putExtra(Intent.EXTRA_USER, mDialogInfo.user);
+ newIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+ newIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+ newIntent.putExtra(UninstallUninstalling.EXTRA_APP_LABEL, label);
+ newIntent.putExtra(PackageInstaller.EXTRA_CALLBACK, mDialogInfo.callback);
+
+ if (returnResult) {
+ newIntent.putExtra(Intent.EXTRA_RETURN_RESULT, true);
+ }
+
+ if (returnResult || getCallingActivity() != null) {
+ newIntent.addFlags(Intent.FLAG_ACTIVITY_FORWARD_RESULT);
+ }
+
+ startActivity(newIntent);
+ } else {
+ int uninstallId;
+ try {
+ uninstallId = UninstallEventReceiver.getNewId(this);
+ } catch (EventResultPersister.OutOfIdsException e) {
+ showGenericError();
+ return;
+ }
+
+ Intent broadcastIntent = new Intent(this, UninstallFinish.class);
+
+ broadcastIntent.setFlags(Intent.FLAG_RECEIVER_FOREGROUND);
+ broadcastIntent.putExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, mDialogInfo.allUsers);
+ broadcastIntent.putExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO, mDialogInfo.appInfo);
+ broadcastIntent.putExtra(UninstallFinish.EXTRA_APP_LABEL, label);
+ broadcastIntent.putExtra(UninstallFinish.EXTRA_UNINSTALL_ID, uninstallId);
+
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(this, uninstallId,
+ broadcastIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ NotificationChannel uninstallingChannel = new NotificationChannel(UNINSTALLING_CHANNEL,
+ getString(R.string.uninstalling_notification_channel),
+ NotificationManager.IMPORTANCE_MIN);
+ notificationManager.createNotificationChannel(uninstallingChannel);
+
+ Notification uninstallingNotification =
+ (new Notification.Builder(this, UNINSTALLING_CHANNEL))
+ .setSmallIcon(R.drawable.ic_remove).setProgress(0, 1, true)
+ .setContentTitle(getString(R.string.uninstalling_app, label)).setOngoing(true)
+ .build();
+
+ notificationManager.notify(uninstallId, uninstallingNotification);
+
+ try {
+ Log.i(TAG, "Uninstalling extras=" + broadcastIntent.getExtras());
+
+ ActivityThread.getPackageManager().getPackageInstaller().uninstall(
+ new VersionedPackage(mDialogInfo.appInfo.packageName,
+ PackageManager.VERSION_CODE_HIGHEST),
+ getPackageName(), mDialogInfo.allUsers
+ ? PackageManager.DELETE_ALL_USERS : 0,
+ pendingIntent.getIntentSender(), mDialogInfo.user.getIdentifier());
+ } catch (Exception e) {
+ notificationManager.cancel(uninstallId);
+
+ Log.e(TAG, "Cannot start uninstall", e);
+ showGenericError();
+ }
+ }
+ }
+
+ public void dispatchAborted() {
+ if (mDialogInfo != null && mDialogInfo.callback != null) {
+ final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub.asInterface(
+ mDialogInfo.callback);
+ try {
+ observer.onPackageDeleted(mPackageName,
+ PackageManager.DELETE_FAILED_ABORTED, "Cancelled by user");
+ } catch (RemoteException ignored) {
+ }
+ }
+ }
+
+ private String getPackageNameForUid(int sourceUid) {
+ String[] packagesForUid = getPackageManager().getPackagesForUid(sourceUid);
+ if (packagesForUid == null) {
+ return null;
+ }
+ return packagesForUid[0];
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java
new file mode 100644
index 0000000000000..4ec6a2d689aa6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/ErrorDialogFragment.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.handheld;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.os.Bundle;
+
+import com.android.packageinstaller.UninstallerActivity;
+
+public class ErrorDialogFragment extends DialogFragment {
+ public static final String TITLE = "com.android.packageinstaller.arg.title";
+ public static final String TEXT = "com.android.packageinstaller.arg.text";
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder b = new AlertDialog.Builder(getActivity())
+ .setMessage(getArguments().getInt(TEXT))
+ .setPositiveButton(android.R.string.ok, null);
+
+ if (getArguments().containsKey(TITLE)) {
+ b.setTitle(getArguments().getInt(TITLE));
+ }
+
+ return b.create();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (isAdded()) {
+ if (getActivity() instanceof UninstallerActivity) {
+ ((UninstallerActivity) getActivity()).dispatchAborted();
+ }
+
+ getActivity().setResult(Activity.RESULT_FIRST_USER);
+ getActivity().finish();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
new file mode 100644
index 0000000000000..e0ca74eb99c99
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/handheld/UninstallAlertDialogFragment.java
@@ -0,0 +1,109 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.handheld;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.content.DialogInterface;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+public class UninstallAlertDialogFragment extends DialogFragment implements
+ DialogInterface.OnClickListener {
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ final PackageManager pm = getActivity().getPackageManager();
+ final UninstallerActivity.DialogInfo dialogInfo =
+ ((UninstallerActivity) getActivity()).getDialogInfo();
+ final CharSequence appLabel = dialogInfo.appInfo.loadSafeLabel(pm);
+ AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(getActivity());
+ StringBuilder messageBuilder = new StringBuilder();
+
+ // If the Activity label differs from the App label, then make sure the user
+ // knows the Activity belongs to the App being uninstalled.
+ if (dialogInfo.activityInfo != null) {
+ final CharSequence activityLabel = dialogInfo.activityInfo.loadSafeLabel(pm);
+ if (!activityLabel.equals(appLabel)) {
+ messageBuilder.append(
+ getString(R.string.uninstall_activity_text, activityLabel));
+ messageBuilder.append(" ").append(appLabel).append(".\n\n");
+ }
+ }
+
+ final boolean isUpdate =
+ ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ UserManager userManager = UserManager.get(getActivity());
+ if (isUpdate) {
+ if (isSingleUser(userManager)) {
+ messageBuilder.append(getString(R.string.uninstall_update_text));
+ } else {
+ messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
+ }
+ } else {
+ if (dialogInfo.allUsers && !isSingleUser(userManager)) {
+ messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
+ } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ } else {
+ messageBuilder.append(getString(R.string.uninstall_application_text));
+ }
+ }
+
+ dialogBuilder.setTitle(appLabel);
+ dialogBuilder.setPositiveButton(android.R.string.ok, this);
+ dialogBuilder.setNegativeButton(android.R.string.cancel, this);
+ dialogBuilder.setMessage(messageBuilder.toString());
+ return dialogBuilder.create();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ if (which == Dialog.BUTTON_POSITIVE) {
+ ((UninstallerActivity) getActivity()).startUninstallProgress();
+ } else {
+ ((UninstallerActivity) getActivity()).dispatchAborted();
+ }
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ super.onDismiss(dialog);
+ if (isAdded()) {
+ getActivity().finish();
+ }
+ }
+
+ /**
+ * Returns whether there is only one user on this device, not including
+ * the system-only user.
+ */
+ private boolean isSingleUser(UserManager userManager) {
+ final int userCount = userManager.getUserCount();
+ return userCount == 1
+ || (UserManager.isSplitSystemUser() && userCount == 2);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
new file mode 100644
index 0000000000000..6b3a76071f586
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissionGroup.java
@@ -0,0 +1,1001 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import static android.app.AppOpsManager.MODE_ALLOWED;
+import static android.app.AppOpsManager.MODE_FOREGROUND;
+import static android.app.AppOpsManager.MODE_IGNORED;
+
+import android.annotation.StringRes;
+import android.annotation.SystemApi;
+import android.app.ActivityManager;
+import android.app.AppOpsManager;
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.content.res.ResourceId;
+import android.os.Build;
+import android.os.Process;
+import android.os.UserHandle;
+import android.util.ArrayMap;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.utils.ArrayUtils;
+import com.android.packageinstaller.permission.utils.LocationUtils;
+
+import java.text.Collator;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * All permissions of a permission group that are requested by an app.
+ *
+ *
Some permissions only grant access to the protected resource while the app is running in the
+ * foreground. These permissions are considered "split" into this foreground and a matching
+ * "background" permission.
+ *
+ *
All background permissions of the group are not in the main group and will not be affected
+ * by operations on the group. The background permissions can be found in the {@link
+ * #getBackgroundPermissions() background permissions group}.
+ */
+public final class AppPermissionGroup implements Comparable {
+ private static final String PLATFORM_PACKAGE_NAME = "android";
+
+ private static final String KILL_REASON_APP_OP_CHANGE = "Permission related app op changed";
+
+ private final Context mContext;
+ private final UserHandle mUserHandle;
+ private final PackageManager mPackageManager;
+ private final AppOpsManager mAppOps;
+ private final ActivityManager mActivityManager;
+ private final Collator mCollator;
+
+ private final PackageInfo mPackageInfo;
+ private final String mName;
+ private final String mDeclaringPackage;
+ private final CharSequence mLabel;
+ private final @StringRes int mRequest;
+ private final @StringRes int mRequestDetail;
+ private final @StringRes int mBackgroundRequest;
+ private final @StringRes int mBackgroundRequestDetail;
+ private final CharSequence mDescription;
+ private final ArrayMap mPermissions = new ArrayMap<>();
+ private final String mIconPkg;
+ private final int mIconResId;
+
+ /**
+ * Some permissions are split into foreground and background permission. All non-split and
+ * foreground permissions are in {@link #mPermissions}, all background permissions are in
+ * this field.
+ */
+ private AppPermissionGroup mBackgroundPermissions;
+
+ private final boolean mAppSupportsRuntimePermissions;
+ private final boolean mIsEphemeralApp;
+ private boolean mContainsEphemeralPermission;
+ private boolean mContainsPreRuntimePermission;
+
+ /**
+ * Does this group contain at least one permission that is split into a foreground and
+ * background permission? This does not necessarily mean that the app also requested the
+ * background permission.
+ */
+ private boolean mHasPermissionWithBackgroundMode;
+
+ public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
+ String permissionName) {
+ PermissionInfo permissionInfo;
+ try {
+ permissionInfo = context.getPackageManager().getPermissionInfo(permissionName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+
+ if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS
+ || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (permissionInfo.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ return null;
+ }
+
+ PackageItemInfo groupInfo = permissionInfo;
+ if (permissionInfo.group != null) {
+ try {
+ groupInfo = context.getPackageManager().getPermissionGroupInfo(
+ permissionInfo.group, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+
+ List permissionInfos = null;
+ if (groupInfo instanceof PermissionGroupInfo) {
+ try {
+ permissionInfos = context.getPackageManager().queryPermissionsByGroup(
+ groupInfo.name, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ /* ignore */
+ }
+ }
+
+ return create(context, packageInfo, groupInfo, permissionInfos,
+ Process.myUserHandle());
+ }
+
+ public static AppPermissionGroup create(Context context, PackageInfo packageInfo,
+ PackageItemInfo groupInfo, List permissionInfos,
+ UserHandle userHandle) {
+
+ AppPermissionGroup group = new AppPermissionGroup(context, packageInfo, groupInfo.name,
+ groupInfo.packageName, groupInfo.loadLabel(context.getPackageManager()),
+ loadGroupDescription(context, groupInfo), getRequest(groupInfo),
+ getRequestDetail(groupInfo), getBackgroundRequest(groupInfo),
+ getBackgroundRequestDetail(groupInfo), groupInfo.packageName, groupInfo.icon,
+ userHandle);
+
+ if (groupInfo instanceof PermissionInfo) {
+ permissionInfos = new ArrayList<>();
+ permissionInfos.add((PermissionInfo) groupInfo);
+ }
+
+ if (permissionInfos == null || permissionInfos.isEmpty()) {
+ return null;
+ }
+
+ // Parse and create permissions reqested by the app
+ ArrayMap allPermissions = new ArrayMap<>();
+ final int permissionCount = packageInfo.requestedPermissions.length;
+ for (int i = 0; i < permissionCount; i++) {
+ String requestedPermission = packageInfo.requestedPermissions[i];
+
+ PermissionInfo requestedPermissionInfo = null;
+
+ for (PermissionInfo permissionInfo : permissionInfos) {
+ if (requestedPermission.equals(permissionInfo.name)) {
+ requestedPermissionInfo = permissionInfo;
+ break;
+ }
+ }
+
+ if (requestedPermissionInfo == null) {
+ continue;
+ }
+
+ // Collect only runtime permissions.
+ if ((requestedPermissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS) {
+ continue;
+ }
+
+ // Don't allow toggling non-platform permission groups for legacy apps via app ops.
+ if (packageInfo.applicationInfo.targetSdkVersion <= Build.VERSION_CODES.LOLLIPOP_MR1
+ && !PLATFORM_PACKAGE_NAME.equals(groupInfo.packageName)) {
+ continue;
+ }
+
+ final boolean granted = (packageInfo.requestedPermissionsFlags[i]
+ & PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0;
+
+ final String appOp = PLATFORM_PACKAGE_NAME.equals(requestedPermissionInfo.packageName)
+ ? AppOpsManager.permissionToOp(requestedPermissionInfo.name) : null;
+
+ final boolean appOpAllowed = appOp != null
+ && context.getSystemService(AppOpsManager.class).checkOpNoThrow(appOp,
+ packageInfo.applicationInfo.uid, packageInfo.packageName)
+ == MODE_ALLOWED;
+
+ final int flags = context.getPackageManager().getPermissionFlags(
+ requestedPermission, packageInfo.packageName, userHandle);
+
+ Permission permission = new Permission(requestedPermission,
+ requestedPermissionInfo.backgroundPermission, granted,
+ appOp, appOpAllowed, flags, requestedPermissionInfo.protectionLevel);
+
+ if (requestedPermissionInfo.backgroundPermission != null) {
+ group.mHasPermissionWithBackgroundMode = true;
+ }
+
+ allPermissions.put(requestedPermission, permission);
+ }
+
+ int numPermissions = allPermissions.size();
+ if (numPermissions == 0) {
+ return null;
+ }
+
+ // Link up foreground and background permissions
+ for (int i = 0; i < allPermissions.size(); i++) {
+ Permission permission = allPermissions.valueAt(i);
+
+ if (permission.getBackgroundPermissionName() != null) {
+ Permission backgroundPermission = allPermissions.get(
+ permission.getBackgroundPermissionName());
+
+ if (backgroundPermission != null) {
+ backgroundPermission.addForegroundPermissions(permission);
+ permission.setBackgroundPermission(backgroundPermission);
+ }
+ }
+ }
+
+ // Add permissions found to this group
+ for (int i = 0; i < numPermissions; i++) {
+ Permission permission = allPermissions.valueAt(i);
+
+ if (permission.isBackgroundPermission()) {
+ if (group.getBackgroundPermissions() == null) {
+ group.mBackgroundPermissions = new AppPermissionGroup(group.mContext,
+ group.getApp(), group.getName(), group.getDeclaringPackage(),
+ group.getLabel(), group.getDescription(), group.getRequest(),
+ group.getRequestDetail(), group.getBackgroundRequest(),
+ group.getBackgroundRequestDetail(), group.getIconPkg(),
+ group.getIconResId(), UserHandle.of(group.getUserId()));
+ }
+
+ group.getBackgroundPermissions().addPermission(permission);
+ } else {
+ group.addPermission(permission);
+ }
+ }
+
+ return group;
+ }
+
+ private static @StringRes int getRequest(PackageItemInfo group) {
+ if (group instanceof PermissionGroupInfo) {
+ return ((PermissionGroupInfo) group).requestRes;
+ } else if (group instanceof PermissionInfo) {
+ return ((PermissionInfo) group).requestRes;
+ } else {
+ return 0;
+ }
+ }
+
+ private static CharSequence loadGroupDescription(Context context, PackageItemInfo group) {
+ CharSequence description = null;
+ if (group instanceof PermissionGroupInfo) {
+ description = ((PermissionGroupInfo) group).loadDescription(
+ context.getPackageManager());
+ } else if (group instanceof PermissionInfo) {
+ description = ((PermissionInfo) group).loadDescription(
+ context.getPackageManager());
+ }
+
+ if (description == null || description.length() <= 0) {
+ description = context.getString(R.string.default_permission_description);
+ }
+
+ return description;
+ }
+
+ private AppPermissionGroup(Context context, PackageInfo packageInfo, String name,
+ String declaringPackage, CharSequence label, CharSequence description,
+ @StringRes int request, @StringRes int requestDetail,
+ @StringRes int backgroundRequest, @StringRes int backgroundRequestDetail,
+ String iconPkg, int iconResId, UserHandle userHandle) {
+ mContext = context;
+ mUserHandle = userHandle;
+ mPackageManager = mContext.getPackageManager();
+ mPackageInfo = packageInfo;
+ mAppSupportsRuntimePermissions = packageInfo.applicationInfo
+ .targetSdkVersion > Build.VERSION_CODES.LOLLIPOP_MR1;
+ mIsEphemeralApp = packageInfo.applicationInfo.isInstantApp();
+ mAppOps = context.getSystemService(AppOpsManager.class);
+ mActivityManager = context.getSystemService(ActivityManager.class);
+ mDeclaringPackage = declaringPackage;
+ mName = name;
+ mLabel = label;
+ mDescription = description;
+ mCollator = Collator.getInstance(
+ context.getResources().getConfiguration().getLocales().get(0));
+ mRequest = request;
+ mRequestDetail = requestDetail;
+ mBackgroundRequest = backgroundRequest;
+ mBackgroundRequestDetail = backgroundRequestDetail;
+ if (iconResId != 0) {
+ mIconPkg = iconPkg;
+ mIconResId = iconResId;
+ } else {
+ mIconPkg = context.getPackageName();
+ mIconResId = R.drawable.ic_perm_device_info;
+ }
+ }
+
+ public boolean doesSupportRuntimePermissions() {
+ return mAppSupportsRuntimePermissions;
+ }
+
+ public boolean isGrantingAllowed() {
+ return (!mIsEphemeralApp || mContainsEphemeralPermission)
+ && (mAppSupportsRuntimePermissions || mContainsPreRuntimePermission);
+ }
+
+ public boolean isReviewRequired() {
+ if (mAppSupportsRuntimePermissions) {
+ return false;
+ }
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isReviewRequired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public void resetReviewRequired() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isReviewRequired()) {
+ permission.resetReviewRequired();
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED,
+ 0, mUserHandle);
+ }
+ }
+ }
+
+ public boolean hasGrantedByDefaultPermission() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isGrantedByDefault()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public PackageInfo getApp() {
+ return mPackageInfo;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getDeclaringPackage() {
+ return mDeclaringPackage;
+ }
+
+ public String getIconPkg() {
+ return mIconPkg;
+ }
+
+ public int getIconResId() {
+ return mIconResId;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ /**
+ * @hide
+ * @return The resource Id of the request string.
+ */
+ @SystemApi
+ public @StringRes int getRequest() {
+ return mRequest;
+ }
+
+ /**
+ * Extract the (subtitle) message explaining to the user that the permission is only granted to
+ * the apps running in the foreground.
+ *
+ * @param info The package item info to extract the message from
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ private static @StringRes int getRequestDetail(PackageItemInfo info) {
+ if (info instanceof PermissionGroupInfo) {
+ return ((PermissionGroupInfo) info).requestDetailResourceId;
+ } else {
+ return ResourceId.ID_NULL;
+ }
+ }
+
+ /**
+ * Get the (subtitle) message explaining to the user that the permission is only granted to
+ * the apps running in the foreground.
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ public @StringRes int getRequestDetail() {
+ return mRequestDetail;
+ }
+
+ /**
+ * Extract the title of the dialog explaining to the user that the permission is granted while
+ * the app is in background and in foreground.
+ *
+ * @param info The package item info to extract the message from
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ private static @StringRes int getBackgroundRequest(PackageItemInfo info) {
+ if (info instanceof PermissionGroupInfo) {
+ return ((PermissionGroupInfo) info).backgroundRequestResourceId;
+ } else {
+ return ResourceId.ID_NULL;
+ }
+ }
+
+ /**
+ * Get the title of the dialog explaining to the user that the permission is granted while
+ * the app is in background and in foreground.
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ public @StringRes int getBackgroundRequest() {
+ return mBackgroundRequest;
+ }
+
+ /**
+ * Extract the (subtitle) message explaining to the user that the she/he is about to allow the
+ * app to have background access.
+ *
+ * @param info The package item info to extract the message from
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ private static @StringRes int getBackgroundRequestDetail(PackageItemInfo info) {
+ if (info instanceof PermissionGroupInfo) {
+ return ((PermissionGroupInfo) info).backgroundRequestDetailResourceId;
+ } else {
+ return ResourceId.ID_NULL;
+ }
+ }
+
+ /**
+ * Get the (subtitle) message explaining to the user that the she/he is about to allow the
+ * app to have background access.
+ *
+ * @return the message of {@link ResourceId#ID_NULL} if unset
+ */
+ public @StringRes int getBackgroundRequestDetail() {
+ return mBackgroundRequestDetail;
+ }
+
+ public CharSequence getDescription() {
+ return mDescription;
+ }
+
+ public int getUserId() {
+ return mUserHandle.getIdentifier();
+ }
+
+ public boolean hasPermission(String permission) {
+ return mPermissions.get(permission) != null;
+ }
+
+ public boolean areRuntimePermissionsGranted() {
+ return areRuntimePermissionsGranted(null);
+ }
+
+ public boolean areRuntimePermissionsGranted(String[] filterPermissions) {
+ if (LocationUtils.isLocationGroupAndProvider(mName, mPackageInfo.packageName)) {
+ return LocationUtils.isLocationEnabled(mContext);
+ }
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (filterPermissions != null
+ && !ArrayUtils.contains(filterPermissions, permission.getName())) {
+ continue;
+ }
+ if (mAppSupportsRuntimePermissions) {
+ if (permission.isGranted()) {
+ return true;
+ }
+ } else if (permission.isGranted()
+ && (!permission.affectsAppOp() || permission.isAppOpAllowed())
+ && !permission.isReviewRequired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean grantRuntimePermissions(boolean fixedByTheUser) {
+ return grantRuntimePermissions(fixedByTheUser, null);
+ }
+
+ /**
+ * Allow the app op for a permission/uid.
+ *
+ *
There are three cases:
+ *
+ *
The permission is not split into foreground/background
+ *
The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}
+ *
The permission is a foreground permission:
+ *
The background permission permission is granted
+ *
The app op matching the permission will be set to {@link AppOpsManager#MODE_ALLOWED}
+ *
The background permission permission is not granted
+ *
The app op matching the permission will be set to
+ * {@link AppOpsManager#MODE_FOREGROUND}
+ *
+ *
The permission is a background permission:
+ *
All granted foreground permissions for this background permission will be set to
+ * {@link AppOpsManager#MODE_ALLOWED}
+ *
+ *
+ * @param permission The permission which has an appOps that should be allowed
+ * @param uid The uid of the process the app op if for
+ */
+ private void allowAppOp(Permission permission, int uid) {
+ if (permission.isBackgroundPermission()) {
+ ArrayList foregroundPermissions = permission.getForegroundPermissions();
+
+ int numForegroundPermissions = foregroundPermissions.size();
+ for (int i = 0; i < numForegroundPermissions; i++) {
+ Permission foregroundPermission = foregroundPermissions.get(i);
+ if (foregroundPermission.isAppOpAllowed()) {
+ mAppOps.setUidMode(foregroundPermission.getAppOp(), uid, MODE_ALLOWED);
+ }
+ }
+ } else {
+ if (permission.hasBackgroundPermission()) {
+ Permission backgroundPermission = permission.getBackgroundPermission();
+
+ if (backgroundPermission == null) {
+ // The app requested a permission that has a background permission but it did
+ // not request the background permission, hence it can never get background
+ // access
+ mAppOps.setUidMode(permission.getAppOp(), uid, MODE_FOREGROUND);
+ } else {
+ if (backgroundPermission.isAppOpAllowed()) {
+ mAppOps.setUidMode(permission.getAppOp(), uid, MODE_ALLOWED);
+ } else {
+ mAppOps.setUidMode(permission.getAppOp(), uid, MODE_FOREGROUND);
+ }
+ }
+ } else {
+ mAppOps.setUidMode(permission.getAppOp(), uid, MODE_ALLOWED);
+ }
+ }
+ }
+
+ /**
+ * Grant permissions of the group.
+ *
+ *
This also automatically grants all app ops for permissions that have app ops.
+ *
This does only grant permissions in {@link #mPermissions}, i.e. usually not
+ * the background permissions.
+ *
+ * @param fixedByTheUser If the user requested that she/he does not want to be asked again
+ * @param filterPermissions If {@code null} all permissions of the group will be granted.
+ * Otherwise only permissions in {@code filterPermissions} will be
+ * granted.
+ *
+ * @return {@code true} iff all permissions of this group could be granted.
+ */
+ public boolean grantRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
+ final int uid = mPackageInfo.applicationInfo.uid;
+
+ // We toggle permissions only to apps that support runtime
+ // permissions, otherwise we toggle the app op corresponding
+ // to the permission if the permission is granted to the app.
+ for (Permission permission : mPermissions.values()) {
+ if (filterPermissions != null
+ && !ArrayUtils.contains(filterPermissions, permission.getName())) {
+ continue;
+ }
+
+ if (!permission.isGrantingAllowed(mIsEphemeralApp, mAppSupportsRuntimePermissions)) {
+ // Skip unallowed permissions.
+ continue;
+ }
+
+ if (mAppSupportsRuntimePermissions) {
+ // Do not touch permissions fixed by the system.
+ if (permission.isSystemFixed()) {
+ return false;
+ }
+
+ // Ensure the permission app op enabled before the permission grant.
+ if (permission.affectsAppOp() && !permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(true);
+ allowAppOp(permission, uid);
+ }
+
+ // Grant the permission if needed.
+ if (!permission.isGranted()) {
+ permission.setGranted(true);
+ mPackageManager.grantRuntimePermission(mPackageInfo.packageName,
+ permission.getName(), mUserHandle);
+ }
+
+ // Update the permission flags.
+ if (!fixedByTheUser) {
+ // Now the apps can ask for the permission as the user
+ // no longer has it fixed in a denied state.
+ if (permission.isUserFixed() || permission.isUserSet()) {
+ permission.setUserFixed(false);
+ permission.setUserSet(false);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_FIXED
+ | PackageManager.FLAG_PERMISSION_USER_SET,
+ 0, mUserHandle);
+ }
+ }
+ } else {
+ // Legacy apps cannot have a not granted permission but just in case.
+ if (!permission.isGranted()) {
+ continue;
+ }
+
+ int killUid = -1;
+ int mask = 0;
+
+ // If the permissions has no corresponding app op, then it is a
+ // third-party one and we do not offer toggling of such permissions.
+ if (permission.affectsAppOp()) {
+ if (!permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(true);
+ allowAppOp(permission, uid);
+
+ // Legacy apps do not know that they have to retry access to a
+ // resource due to changes in runtime permissions (app ops in this
+ // case). Therefore, we restart them on app op change, so they
+ // can pick up the change.
+ killUid = uid;
+ }
+
+ // Mark that the permission should not be be granted on upgrade
+ // when the app begins supporting runtime permissions.
+ if (permission.shouldRevokeOnUpgrade()) {
+ permission.setRevokeOnUpgrade(false);
+ mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ }
+ }
+
+ // Granting a permission explicitly means the user already
+ // reviewed it so clear the review flag on every grant.
+ if (permission.isReviewRequired()) {
+ permission.resetReviewRequired();
+ mask |= PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ if (mask != 0) {
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName, mask, 0, mUserHandle);
+ }
+
+ if (killUid != -1) {
+ mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public boolean revokeRuntimePermissions(boolean fixedByTheUser) {
+ return revokeRuntimePermissions(fixedByTheUser, null);
+ }
+
+ /**
+ * Disallow the app op for a permission/uid.
+ *
+ *
There are three cases:
+ *
+ *
The permission is not split into foreground/background
+ *
The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}
+ *
The permission is a foreground permission:
+ *
The app op matching the permission will be set to {@link AppOpsManager#MODE_IGNORED}
+ *
The permission is a background permission:
+ *
All granted foreground permissions for this background permission will be set to
+ * {@link AppOpsManager#MODE_FOREGROUND}
+ *
+ *
+ * @param permission The permission which has an appOps that should be disallowed
+ * @param uid The uid of the process the app op if for
+ */
+ private void disallowAppOp(Permission permission, int uid) {
+ if (permission.isBackgroundPermission()) {
+ ArrayList foregroundPermissions = permission.getForegroundPermissions();
+
+ int numForegroundPermissions = foregroundPermissions.size();
+ for (int i = 0; i < numForegroundPermissions; i++) {
+ Permission foregroundPermission = foregroundPermissions.get(i);
+ if (foregroundPermission.isAppOpAllowed()) {
+ mAppOps.setUidMode(foregroundPermission.getAppOp(), uid, MODE_FOREGROUND);
+ }
+ }
+ } else {
+ mAppOps.setUidMode(permission.getAppOp(), uid, MODE_IGNORED);
+ }
+ }
+
+ /**
+ * Revoke permissions of the group.
+ *
+ *
This also disallows all app ops for permissions that have app ops.
+ *
This does only revoke permissions in {@link #mPermissions}, i.e. usually not
+ * the background permissions.
+ *
+ * @param fixedByTheUser If the user requested that she/he does not want to be asked again
+ * @param filterPermissions If {@code null} all permissions of the group will be revoked.
+ * Otherwise only permissions in {@code filterPermissions} will be
+ * revoked.
+ *
+ * @return {@code true} iff all permissions of this group could be revoked.
+ */
+ public boolean revokeRuntimePermissions(boolean fixedByTheUser, String[] filterPermissions) {
+ final int uid = mPackageInfo.applicationInfo.uid;
+
+ // We toggle permissions only to apps that support runtime
+ // permissions, otherwise we toggle the app op corresponding
+ // to the permission if the permission is granted to the app.
+ for (Permission permission : mPermissions.values()) {
+ if (filterPermissions != null
+ && !ArrayUtils.contains(filterPermissions, permission.getName())) {
+ continue;
+ }
+
+ if (mAppSupportsRuntimePermissions) {
+ // Do not touch permissions fixed by the system.
+ if (permission.isSystemFixed()) {
+ return false;
+ }
+
+ // Revoke the permission if needed.
+ if (permission.isGranted()) {
+ permission.setGranted(false);
+ mPackageManager.revokeRuntimePermission(mPackageInfo.packageName,
+ permission.getName(), mUserHandle);
+ }
+
+ // Update the permission flags.
+ if (fixedByTheUser) {
+ // Take a note that the user fixed the permission.
+ if (permission.isUserSet() || !permission.isUserFixed()) {
+ permission.setUserSet(false);
+ permission.setUserFixed(true);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_USER_FIXED,
+ PackageManager.FLAG_PERMISSION_USER_FIXED,
+ mUserHandle);
+ }
+ } else {
+ if (!permission.isUserSet() || permission.isUserFixed()) {
+ permission.setUserSet(true);
+ permission.setUserFixed(false);
+ // Take a note that the user already chose once.
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_USER_SET
+ | PackageManager.FLAG_PERMISSION_USER_FIXED,
+ PackageManager.FLAG_PERMISSION_USER_SET,
+ mUserHandle);
+ }
+ }
+
+ if (permission.affectsAppOp()) {
+ permission.setAppOpAllowed(false);
+ disallowAppOp(permission, uid);
+ }
+ } else {
+ // Legacy apps cannot have a non-granted permission but just in case.
+ if (!permission.isGranted()) {
+ continue;
+ }
+
+ int mask = 0;
+ int flags = 0;
+ int killUid = -1;
+
+ // If the permission has no corresponding app op, then it is a
+ // third-party one and we do not offer toggling of such permissions.
+ if (permission.affectsAppOp()) {
+ if (permission.isAppOpAllowed()) {
+ permission.setAppOpAllowed(false);
+ disallowAppOp(permission, uid);
+
+ // Disabling an app op may put the app in a situation in which it
+ // has a handle to state it shouldn't have, so we have to kill the
+ // app. This matches the revoke runtime permission behavior.
+ killUid = uid;
+ }
+
+ // Mark that the permission should not be granted on upgrade
+ // when the app begins supporting runtime permissions.
+ if (!permission.shouldRevokeOnUpgrade()) {
+ permission.setRevokeOnUpgrade(true);
+ mask |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ flags |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ }
+ }
+
+ if (mask != 0) {
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName, mask, flags, mUserHandle);
+ }
+
+ if (killUid != -1) {
+ mActivityManager.killUid(uid, KILL_REASON_APP_OP_CHANGE);
+ }
+ }
+ }
+
+ return true;
+ }
+
+ public void setPolicyFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ permission.setPolicyFixed(true);
+ mPackageManager.updatePermissionFlags(permission.getName(),
+ mPackageInfo.packageName,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+ PackageManager.FLAG_PERMISSION_POLICY_FIXED,
+ mUserHandle);
+ }
+ }
+
+ public ArrayList getPermissions() {
+ return new ArrayList<>(mPermissions.values());
+ }
+
+ /**
+ * @return An {@link AppPermissionGroup}-object that contains all background permissions for
+ * this group.
+ */
+ public AppPermissionGroup getBackgroundPermissions() {
+ return mBackgroundPermissions;
+ }
+
+ /**
+ * @return {@code true} iff the app request at least one permission in this group that has a
+ * background permission. It is possible that the app does not request the matching background
+ * permission and hence will only ever get foreground access, never background access.
+ */
+ public boolean hasPermissionWithBackgroundMode() {
+ return mHasPermissionWithBackgroundMode;
+ }
+
+ /**
+ * Whether this is group that contains all the background permission for regular permission
+ * group.
+ *
+ * @return {@code true} iff this is a background permission group.
+ *
+ * @see #getBackgroundPermissions()
+ */
+ public boolean isBackgroundGroup() {
+ return mPermissions.valueAt(0).isBackgroundPermission();
+ }
+
+ public int getFlags() {
+ int flags = 0;
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ flags |= permission.getFlags();
+ }
+ return flags;
+ }
+
+ public boolean isUserFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isUserFixed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isPolicyFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isPolicyFixed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isUserSet() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isUserSet()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ public boolean isSystemFixed() {
+ final int permissionCount = mPermissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = mPermissions.valueAt(i);
+ if (permission.isSystemFixed()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public int compareTo(AppPermissionGroup another) {
+ final int result = mCollator.compare(mLabel.toString(), another.mLabel.toString());
+ if (result == 0) {
+ // Unbadged before badged.
+ return mPackageInfo.applicationInfo.uid
+ - another.mPackageInfo.applicationInfo.uid;
+ }
+ return result;
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (o == null || !(o instanceof AppPermissionGroup)) {
+ return false;
+ }
+
+ AppPermissionGroup other = (AppPermissionGroup) o;
+ return mName.equals(other.mName)
+ && mPackageInfo.packageName.equals(other.mPackageInfo.packageName)
+ && mUserHandle.equals(other.mUserHandle);
+ }
+
+ @Override
+ public int hashCode() {
+ return mName.hashCode() + mPackageInfo.packageName.hashCode() + mUserHandle.hashCode();
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder builder = new StringBuilder();
+ builder.append(getClass().getSimpleName());
+ builder.append("{name=").append(mName);
+ if (mBackgroundPermissions != null) {
+ builder.append(", }");
+ }
+ if (!mPermissions.isEmpty()) {
+ builder.append(", }");
+ } else {
+ builder.append('}');
+ }
+ return builder.toString();
+ }
+
+ private void addPermission(Permission permission) {
+ mPermissions.put(permission.getName(), permission);
+ if (permission.isEphemeral()) {
+ mContainsEphemeralPermission = true;
+ }
+ if (!permission.isRuntimeOnly()) {
+ mContainsPreRuntimePermission = true;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissions.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissions.java
new file mode 100644
index 0000000000000..880ff26ebec4a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/AppPermissions.java
@@ -0,0 +1,175 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import android.content.Context;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.text.BidiFormatter;
+import android.util.ArrayMap;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+/**
+ * An app that requests permissions.
+ *
+ *
Allows to query all permission groups of the app and which permission belongs to which group.
+ */
+public final class AppPermissions {
+ /**
+ * All permission groups the app requests. Background permission groups are attached to their
+ * foreground groups.
+ */
+ private final ArrayList mGroups = new ArrayList<>();
+
+ /** Cache: group name -> group */
+ private final ArrayMap mGroupNameToGroup = new ArrayMap<>();
+
+ /** Cache: permission name -> group. Might point to background group */
+ private final ArrayMap mPermissionNameToGroup = new ArrayMap<>();
+
+ private final Context mContext;
+
+ private final CharSequence mAppLabel;
+
+ private final Runnable mOnErrorCallback;
+
+ private final boolean mSortGroups;
+
+ private PackageInfo mPackageInfo;
+
+ public AppPermissions(Context context, PackageInfo packageInfo, boolean sortGroups,
+ Runnable onErrorCallback) {
+ mContext = context;
+ mPackageInfo = packageInfo;
+ mAppLabel = BidiFormatter.getInstance().unicodeWrap(
+ packageInfo.applicationInfo.loadSafeLabel(context.getPackageManager(),
+ PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+ PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE)
+ .toString());
+ mSortGroups = sortGroups;
+ mOnErrorCallback = onErrorCallback;
+ loadPermissionGroups();
+ }
+
+ public PackageInfo getPackageInfo() {
+ return mPackageInfo;
+ }
+
+ public void refresh() {
+ loadPackageInfo();
+ loadPermissionGroups();
+ }
+
+ public CharSequence getAppLabel() {
+ return mAppLabel;
+ }
+
+ public AppPermissionGroup getPermissionGroup(String name) {
+ return mGroupNameToGroup.get(name);
+ }
+
+ public List getPermissionGroups() {
+ return mGroups;
+ }
+
+ public boolean isReviewRequired() {
+ final int groupCount = mGroups.size();
+ for (int i = 0; i < groupCount; i++) {
+ AppPermissionGroup group = mGroups.get(i);
+ if (group.isReviewRequired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void loadPackageInfo() {
+ try {
+ mPackageInfo = mContext.getPackageManager().getPackageInfo(
+ mPackageInfo.packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (mOnErrorCallback != null) {
+ mOnErrorCallback.run();
+ }
+ }
+ }
+
+ /**
+ * Add all individual permissions of the {@code group} to the {@link #mPermissionNameToGroup}
+ * lookup table.
+ *
+ * @param group The group of permissions to add
+ */
+ private void addAllPermissions(AppPermissionGroup group) {
+ ArrayList perms = group.getPermissions();
+
+ int numPerms = perms.size();
+ for (int permNum = 0; permNum < numPerms; permNum++) {
+ mPermissionNameToGroup.put(perms.get(permNum).getName(), group);
+ }
+ }
+
+ private void loadPermissionGroups() {
+ mGroups.clear();
+ mGroupNameToGroup.clear();
+ mPermissionNameToGroup.clear();
+
+ if (mPackageInfo.requestedPermissions != null) {
+ for (String requestedPerm : mPackageInfo.requestedPermissions) {
+ if (getGroupForPermission(requestedPerm) == null) {
+ AppPermissionGroup group = AppPermissionGroup.create(mContext, mPackageInfo,
+ requestedPerm);
+ if (group == null) {
+ continue;
+ }
+
+ mGroups.add(group);
+ mGroupNameToGroup.put(group.getName(), group);
+
+ addAllPermissions(group);
+
+ AppPermissionGroup backgroundGroup = group.getBackgroundPermissions();
+ if (backgroundGroup != null) {
+ addAllPermissions(backgroundGroup);
+ }
+ }
+ }
+
+ if (mSortGroups) {
+ Collections.sort(mGroups);
+ }
+ }
+ }
+
+ /**
+ * Find the group a permission belongs to.
+ *
+ *
The group found might be a background group.
+ *
+ * @param permission The name of the permission
+ *
+ * @return The group the permission belongs to
+ */
+ public AppPermissionGroup getGroupForPermission(String permission) {
+ return mPermissionNameToGroup.get(permission);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/Permission.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/Permission.java
new file mode 100644
index 0000000000000..2e555f32e289f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/Permission.java
@@ -0,0 +1,233 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+
+import java.util.ArrayList;
+
+/**
+ * A permission and it's properties.
+ *
+ * @see AppPermissionGroup
+ */
+public final class Permission {
+ private final String mName;
+ private final String mBackgroundPermissionName;
+ private final String mAppOp;
+
+ private boolean mGranted;
+ private boolean mAppOpAllowed;
+ private int mFlags;
+ private boolean mIsEphemeral;
+ private boolean mIsRuntimeOnly;
+ private Permission mBackgroundPermission;
+ private ArrayList mForegroundPermissions;
+
+ public Permission(String name, String backgroundPermissionName, boolean granted,
+ String appOp, boolean appOpAllowed, int flags, int protectionLevel) {
+ mName = name;
+ mBackgroundPermissionName = backgroundPermissionName;
+ mGranted = granted;
+ mAppOp = appOp;
+ mAppOpAllowed = appOpAllowed;
+ mFlags = flags;
+ mIsEphemeral = (protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) != 0;
+ mIsRuntimeOnly = (protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY) != 0;
+ }
+
+ /**
+ * Mark this permission as background permission for {@code foregroundPermissions}.
+ *
+ * @param foregroundPermission The foreground permission
+ */
+ public void addForegroundPermissions(Permission foregroundPermission) {
+ if (mForegroundPermissions == null) {
+ mForegroundPermissions = new ArrayList<>(1);
+ }
+ mForegroundPermissions.add(foregroundPermission);
+ }
+
+ /**
+ * Mark this permission as foreground permission for {@code backgroundPermission}.
+ *
+ * @param backgroundPermission The background permission
+ */
+ public void setBackgroundPermission(Permission backgroundPermission) {
+ mBackgroundPermission = backgroundPermission;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getAppOp() {
+ return mAppOp;
+ }
+
+ public int getFlags() {
+ return mFlags;
+ }
+
+ /**
+ * Does this permission affect app ops.
+ *
+ *
I.e. does this permission have a matching app op or is this a background permission. All
+ * background permissions affect the app op of it's assigned foreground permission.
+ *
+ * @return {@code true} if this permission affects app ops
+ */
+ public boolean affectsAppOp() {
+ return mAppOp != null || isBackgroundPermission();
+ }
+
+ public boolean isGranted() {
+ return mGranted;
+ }
+
+ public boolean isReviewRequired() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED) != 0;
+ }
+
+ public void resetReviewRequired() {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_REVIEW_REQUIRED;
+ }
+
+ public void setGranted(boolean mGranted) {
+ this.mGranted = mGranted;
+ }
+
+ public boolean isAppOpAllowed() {
+ return mAppOpAllowed;
+ }
+
+ public boolean isUserFixed() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_USER_FIXED) != 0;
+ }
+
+ public void setUserFixed(boolean userFixed) {
+ if (userFixed) {
+ mFlags |= PackageManager.FLAG_PERMISSION_USER_FIXED;
+ } else {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_USER_FIXED;
+ }
+ }
+
+ public boolean isSystemFixed() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_SYSTEM_FIXED) != 0;
+ }
+
+ public boolean isPolicyFixed() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_POLICY_FIXED) != 0;
+ }
+
+ public boolean isUserSet() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_USER_SET) != 0;
+ }
+
+ public boolean isGrantedByDefault() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_GRANTED_BY_DEFAULT) != 0;
+ }
+
+ /**
+ * If this permission is split into a foreground and background permission, this is the name
+ * of the background permission.
+ *
+ * @return The name of the background permission or {@code null} if the permission is not split
+ */
+ public String getBackgroundPermissionName() {
+ return mBackgroundPermissionName;
+ }
+
+ /**
+ * @return If this permission is split into a foreground and background permission,
+ * returns the background permission
+ */
+ public Permission getBackgroundPermission() {
+ return mBackgroundPermission;
+ }
+
+ /**
+ * @return If this permission is split into a foreground and background permission,
+ * returns the foreground permission
+ */
+ public ArrayList getForegroundPermissions() {
+ return mForegroundPermissions;
+ }
+
+ /**
+ * @return {@code true} iff this is the foreground permission of a background-foreground-split
+ * permission
+ */
+ public boolean hasBackgroundPermission() {
+ return mBackgroundPermissionName != null;
+ }
+
+ /**
+ * @return {@code true} iff this is the background permission of a background-foreground-split
+ * permission
+ */
+ public boolean isBackgroundPermission() {
+ return mForegroundPermissions != null;
+ }
+
+ public void setUserSet(boolean userSet) {
+ if (userSet) {
+ mFlags |= PackageManager.FLAG_PERMISSION_USER_SET;
+ } else {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_USER_SET;
+ }
+ }
+
+ public void setPolicyFixed(boolean policyFixed) {
+ if (policyFixed) {
+ mFlags |= PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ } else {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_POLICY_FIXED;
+ }
+ }
+
+ public boolean shouldRevokeOnUpgrade() {
+ return (mFlags & PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE) != 0;
+ }
+
+ public void setRevokeOnUpgrade(boolean revokeOnUpgrade) {
+ if (revokeOnUpgrade) {
+ mFlags |= PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ } else {
+ mFlags &= ~PackageManager.FLAG_PERMISSION_REVOKE_ON_UPGRADE;
+ }
+ }
+
+ public void setAppOpAllowed(boolean mAppOpAllowed) {
+ this.mAppOpAllowed = mAppOpAllowed;
+ }
+
+ public boolean isEphemeral() {
+ return mIsEphemeral;
+ }
+
+ public boolean isRuntimeOnly() {
+ return mIsRuntimeOnly;
+ }
+
+ public boolean isGrantingAllowed(boolean isEphemeralApp, boolean supportsRuntimePermissions) {
+ return (!isEphemeralApp || isEphemeral())
+ && (supportsRuntimePermissions || !isRuntimeOnly());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionApps.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionApps.java
new file mode 100644
index 0000000000000..d0bf5853b77bb
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionApps.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.os.AsyncTask;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.util.SparseArray;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class PermissionApps {
+ private static final String LOG_TAG = "PermissionApps";
+
+ private final Context mContext;
+ private final String mGroupName;
+ private final PackageManager mPm;
+ private final Callback mCallback;
+
+ private final PmCache mCache;
+
+ private CharSequence mLabel;
+ private Drawable mIcon;
+ private List mPermApps;
+ // Map (pkg|uid) -> AppPermission
+ private ArrayMap mAppLookup;
+
+ private boolean mSkipUi;
+ private boolean mRefreshing;
+
+ public PermissionApps(Context context, String groupName, Callback callback) {
+ this(context, groupName, callback, null);
+ }
+
+ public PermissionApps(Context context, String groupName, Callback callback, PmCache cache) {
+ mCache = cache;
+ mContext = context;
+ mPm = mContext.getPackageManager();
+ mGroupName = groupName;
+ mCallback = callback;
+ loadGroupInfo();
+ }
+
+ public String getGroupName() {
+ return mGroupName;
+ }
+
+ public void loadNowWithoutUi() {
+ mSkipUi = true;
+ createMap(loadPermissionApps());
+ }
+
+ /**
+ * Start an async refresh and call back the registered call back once done.
+ *
+ * @param getUiInfo If the UI info should be updated
+ */
+ public void refresh(boolean getUiInfo) {
+ if (mCallback == null) {
+ throw new IllegalStateException("callback needs to be set");
+ }
+
+ if (!mRefreshing) {
+ mRefreshing = true;
+ mSkipUi = !getUiInfo;
+ new PermissionAppsLoader().execute();
+ }
+ }
+
+ /**
+ * Refresh the state and do not return until it finishes. Should not be called while an {@link
+ * #refresh async referesh} is in progress.
+ */
+ public void refreshSync() {
+ mSkipUi = true;
+ createMap(loadPermissionApps());
+ }
+
+ public int getGrantedCount(ArraySet launcherPkgs) {
+ int count = 0;
+ for (PermissionApp app : mPermApps) {
+ if (!Utils.shouldShowPermission(app.getPermissionGroup())) {
+ continue;
+ }
+ if (Utils.isSystem(app, launcherPkgs)) {
+ // We default to not showing system apps, so hide them from count.
+ continue;
+ }
+ if (app.areRuntimePermissionsGranted()) {
+ count++;
+ }
+ }
+ return count;
+ }
+
+ public int getTotalCount(ArraySet launcherPkgs) {
+ int count = 0;
+ for (PermissionApp app : mPermApps) {
+ if (!Utils.shouldShowPermission(app.getPermissionGroup())) {
+ continue;
+ }
+ if (Utils.isSystem(app, launcherPkgs)) {
+ // We default to not showing system apps, so hide them from count.
+ continue;
+ }
+ count++;
+ }
+ return count;
+ }
+
+ public List getApps() {
+ return mPermApps;
+ }
+
+ public PermissionApp getApp(String key) {
+ return mAppLookup.get(key);
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ private List loadPermissionApps() {
+ PackageItemInfo groupInfo = getGroupInfo(mGroupName);
+ if (groupInfo == null) {
+ return Collections.emptyList();
+ }
+
+ List groupPermInfos = getGroupPermissionInfos(mGroupName);
+ if (groupPermInfos == null) {
+ return Collections.emptyList();
+ }
+
+ ArrayList permApps = new ArrayList<>();
+ IconDrawableFactory iconFactory = IconDrawableFactory.newInstance(mContext);
+
+ UserManager userManager = mContext.getSystemService(UserManager.class);
+ for (UserHandle user : userManager.getUserProfiles()) {
+ List apps = mCache != null ? mCache.getPackages(user.getIdentifier())
+ : mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS,
+ user.getIdentifier());
+
+ final int N = apps.size();
+ for (int i = 0; i < N; i++) {
+ PackageInfo app = apps.get(i);
+ if (app.requestedPermissions == null) {
+ continue;
+ }
+
+ for (int j = 0; j < app.requestedPermissions.length; j++) {
+ String requestedPerm = app.requestedPermissions[j];
+
+ PermissionInfo requestedPermissionInfo = null;
+
+ for (PermissionInfo groupPermInfo : groupPermInfos) {
+ if (requestedPerm.equals(groupPermInfo.name)) {
+ requestedPermissionInfo = groupPermInfo;
+ break;
+ }
+ }
+
+ if (requestedPermissionInfo == null) {
+ continue;
+ }
+
+ if ((requestedPermissionInfo.protectionLevel
+ & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS
+ || (requestedPermissionInfo.flags
+ & PermissionInfo.FLAG_INSTALLED) == 0
+ || (requestedPermissionInfo.flags
+ & PermissionInfo.FLAG_REMOVED) != 0) {
+ continue;
+ }
+
+ AppPermissionGroup group = AppPermissionGroup.create(mContext,
+ app, groupInfo, groupPermInfos, user);
+
+ if (group == null) {
+ continue;
+ }
+
+ String label = mSkipUi ? app.packageName
+ : app.applicationInfo.loadLabel(mPm).toString();
+
+ Drawable icon = null;
+ if (!mSkipUi) {
+ icon = iconFactory.getBadgedIcon(app.applicationInfo,
+ UserHandle.getUserId(group.getApp().applicationInfo.uid));
+ }
+
+ PermissionApp permApp = new PermissionApp(app.packageName, group, label, icon,
+ app.applicationInfo);
+
+ permApps.add(permApp);
+ break; // move to the next app.
+ }
+ }
+ }
+
+ Collections.sort(permApps);
+
+ return permApps;
+ }
+
+ private void createMap(List result) {
+ mAppLookup = new ArrayMap<>();
+ for (PermissionApp app : result) {
+ mAppLookup.put(app.getKey(), app);
+ }
+ mPermApps = result;
+ }
+
+ private PackageItemInfo getGroupInfo(String groupName) {
+ try {
+ return mContext.getPackageManager().getPermissionGroupInfo(groupName, 0);
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+ try {
+ return mContext.getPackageManager().getPermissionInfo(groupName, 0);
+ } catch (NameNotFoundException e2) {
+ /* ignore */
+ }
+ return null;
+ }
+
+ private List getGroupPermissionInfos(String groupName) {
+ try {
+ return mContext.getPackageManager().queryPermissionsByGroup(groupName, 0);
+ } catch (NameNotFoundException e) {
+ /* ignore */
+ }
+ try {
+ PermissionInfo permissionInfo = mContext.getPackageManager()
+ .getPermissionInfo(groupName, 0);
+ List permissions = new ArrayList<>();
+ permissions.add(permissionInfo);
+ return permissions;
+ } catch (NameNotFoundException e2) {
+ /* ignore */
+ }
+ return null;
+ }
+
+ private void loadGroupInfo() {
+ PackageItemInfo info;
+ try {
+ info = mPm.getPermissionGroupInfo(mGroupName, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ try {
+ PermissionInfo permInfo = mPm.getPermissionInfo(mGroupName, 0);
+ if ((permInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS) {
+ Log.w(LOG_TAG, mGroupName + " is not a runtime permission");
+ return;
+ }
+ info = permInfo;
+ } catch (NameNotFoundException reallyNotFound) {
+ Log.w(LOG_TAG, "Can't find permission: " + mGroupName, reallyNotFound);
+ return;
+ }
+ }
+ mLabel = info.loadLabel(mPm);
+ if (info.icon != 0) {
+ mIcon = info.loadUnbadgedIcon(mPm);
+ } else {
+ mIcon = mContext.getDrawable(R.drawable.ic_perm_device_info);
+ }
+ mIcon = Utils.applyTint(mContext, mIcon, android.R.attr.colorControlNormal);
+ }
+
+ public static class PermissionApp implements Comparable {
+ private final String mPackageName;
+ private final AppPermissionGroup mAppPermissionGroup;
+ private final String mLabel;
+ private final Drawable mIcon;
+ private final ApplicationInfo mInfo;
+
+ public PermissionApp(String packageName, AppPermissionGroup appPermissionGroup,
+ String label, Drawable icon, ApplicationInfo info) {
+ mPackageName = packageName;
+ mAppPermissionGroup = appPermissionGroup;
+ mLabel = label;
+ mIcon = icon;
+ mInfo = info;
+ }
+
+ public ApplicationInfo getAppInfo() {
+ return mInfo;
+ }
+
+ public String getKey() {
+ return mPackageName + getUid();
+ }
+
+ public String getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ public boolean areRuntimePermissionsGranted() {
+ return mAppPermissionGroup.areRuntimePermissionsGranted();
+ }
+
+ public boolean isReviewRequired() {
+ return mAppPermissionGroup.isReviewRequired();
+ }
+
+ public void grantRuntimePermissions() {
+ mAppPermissionGroup.grantRuntimePermissions(false);
+ }
+
+ public void revokeRuntimePermissions() {
+ mAppPermissionGroup.revokeRuntimePermissions(false);
+ }
+
+ public boolean isPolicyFixed() {
+ return mAppPermissionGroup.isPolicyFixed();
+ }
+
+ public boolean isSystemFixed() {
+ return mAppPermissionGroup.isSystemFixed();
+ }
+
+ public boolean hasGrantedByDefaultPermissions() {
+ return mAppPermissionGroup.hasGrantedByDefaultPermission();
+ }
+
+ public boolean doesSupportRuntimePermissions() {
+ return mAppPermissionGroup.doesSupportRuntimePermissions();
+ }
+
+ public int getUserId() {
+ return mAppPermissionGroup.getUserId();
+ }
+
+ public String getPackageName() {
+ return mPackageName;
+ }
+
+ public AppPermissionGroup getPermissionGroup() {
+ return mAppPermissionGroup;
+ }
+
+ @Override
+ public int compareTo(PermissionApp another) {
+ final int result = mLabel.compareTo(another.mLabel);
+ if (result == 0) {
+ // Unbadged before badged.
+ return getKey().compareTo(another.getKey());
+ }
+ return result;
+ }
+
+ public int getUid() {
+ return mAppPermissionGroup.getApp().applicationInfo.uid;
+ }
+ }
+
+ private class PermissionAppsLoader extends AsyncTask> {
+
+ @Override
+ protected List doInBackground(Void... args) {
+ return loadPermissionApps();
+ }
+
+ @Override
+ protected void onPostExecute(List result) {
+ mRefreshing = false;
+ createMap(result);
+ if (mCallback != null) {
+ mCallback.onPermissionsLoaded(PermissionApps.this);
+ }
+ }
+ }
+
+ /**
+ * Class used to reduce the number of calls to the package manager.
+ * This caches app information so it should only be used across parallel PermissionApps
+ * instances, and should not be retained across UI refresh.
+ */
+ public static class PmCache {
+ private final SparseArray> mPackageInfoCache = new SparseArray<>();
+ private final PackageManager mPm;
+
+ public PmCache(PackageManager pm) {
+ mPm = pm;
+ }
+
+ public synchronized List getPackages(int userId) {
+ List ret = mPackageInfoCache.get(userId);
+ if (ret == null) {
+ ret = mPm.getInstalledPackagesAsUser(PackageManager.GET_PERMISSIONS, userId);
+ mPackageInfoCache.put(userId, ret);
+ }
+ return ret;
+ }
+ }
+
+ public interface Callback {
+ void onPermissionsLoaded(PermissionApps permissionApps);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroup.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroup.java
new file mode 100644
index 0000000000000..ebef732f1d597
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroup.java
@@ -0,0 +1,120 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * A permission group with runtime permission as defined in an app's manifest as
+ * {@code android:permission-group}.
+ *
+ *
For individual permissions that are not part of any group a {@link PermissionGroup} is created
+ * dynamically with the name and icon of the individual permission.
+ */
+public final class PermissionGroup implements Comparable {
+ private final String mName;
+ private final String mDeclaringPackage;
+ private final CharSequence mLabel;
+ private final Drawable mIcon;
+ private final int mTotal;
+ private final int mGranted;
+
+ PermissionGroup(String name, String declaringPackage, CharSequence label, Drawable icon,
+ int total, int granted) {
+ mDeclaringPackage = declaringPackage;
+ mName = name;
+ mLabel = label;
+ mIcon = icon;
+ mTotal = total;
+ mGranted = granted;
+ }
+
+ public String getName() {
+ return mName;
+ }
+
+ public String getDeclaringPackage() {
+ return mDeclaringPackage;
+ }
+
+ public CharSequence getLabel() {
+ return mLabel;
+ }
+
+ public Drawable getIcon() {
+ return mIcon;
+ }
+
+ /**
+ * @return The number of apps that might request permissions of this group
+ */
+ public int getTotal() {
+ return mTotal;
+ }
+
+ /**
+ * @return The number of apps that were granted permissions of this group
+ */
+ public int getGranted() {
+ return mGranted;
+ }
+
+ @Override
+ public int compareTo(PermissionGroup another) {
+ return mLabel.toString().compareTo(another.mLabel.toString());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj) {
+ return true;
+ }
+
+ if (obj == null) {
+ return false;
+ }
+
+ if (getClass() != obj.getClass()) {
+ return false;
+ }
+
+ PermissionGroup other = (PermissionGroup) obj;
+
+ if (mName == null) {
+ if (other.mName != null) {
+ return false;
+ }
+ } else if (!mName.equals(other.mName)) {
+ return false;
+ }
+
+ if (mTotal != other.mTotal) {
+ return false;
+ }
+
+ if (mGranted != other.mGranted) {
+ return false;
+ }
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ return mName != null ? mName.hashCode() + mTotal + mGranted : mTotal + mGranted;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroups.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroups.java
new file mode 100644
index 0000000000000..491b059909f13
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/model/PermissionGroups.java
@@ -0,0 +1,262 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.model;
+
+import static android.content.pm.PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE;
+import static android.content.pm.PackageItemInfo.SAFE_LABEL_FLAG_TRIM;
+
+import android.app.LoaderManager;
+import android.app.LoaderManager.LoaderCallbacks;
+import android.content.AsyncTaskLoader;
+import android.content.Context;
+import android.content.Loader;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.ArraySet;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+import java.util.Set;
+
+/**
+ * All {@link PermissionGroup permission groups} defined by any app.
+ */
+public final class PermissionGroups implements LoaderCallbacks> {
+ private final ArrayList mGroups = new ArrayList<>();
+ private final Context mContext;
+ private final LoaderManager mLoaderManager;
+ private final PermissionsGroupsChangeCallback mCallback;
+
+ public interface PermissionsGroupsChangeCallback {
+ public void onPermissionGroupsChanged();
+ }
+
+ public PermissionGroups(Context context, LoaderManager loaderManager,
+ PermissionsGroupsChangeCallback callback) {
+ mContext = context;
+ mLoaderManager = loaderManager;
+ mCallback = callback;
+ mLoaderManager.initLoader(0, null, this);
+ }
+
+ @Override
+ public Loader> onCreateLoader(int id, Bundle args) {
+ return new PermissionsLoader(mContext);
+ }
+
+ @Override
+ public void onLoadFinished(Loader> loader,
+ List groups) {
+ if (mGroups.equals(groups)) {
+ return;
+ }
+ mGroups.clear();
+ mGroups.addAll(groups);
+ mCallback.onPermissionGroupsChanged();
+ }
+
+ @Override
+ public void onLoaderReset(Loader> loader) {
+ mGroups.clear();
+ mCallback.onPermissionGroupsChanged();
+ }
+
+ public List getGroups() {
+ return mGroups;
+ }
+
+ public PermissionGroup getGroup(String name) {
+ for (PermissionGroup group : mGroups) {
+ if (group.getName().equals(name)) {
+ return group;
+ }
+ }
+ return null;
+ }
+
+ private static final class PermissionsLoader extends AsyncTaskLoader>
+ implements PackageManager.OnPermissionsChangedListener {
+
+ public PermissionsLoader(Context context) {
+ super(context);
+ }
+
+ @Override
+ protected void onStartLoading() {
+ getContext().getPackageManager().addOnPermissionsChangeListener(this);
+ forceLoad();
+ }
+
+ @Override
+ protected void onStopLoading() {
+ getContext().getPackageManager().removeOnPermissionsChangeListener(this);
+ }
+
+ @Override
+ public List loadInBackground() {
+ ArraySet launcherPkgs = Utils.getLauncherPackages(getContext());
+ PermissionApps.PmCache pmCache = new PermissionApps.PmCache(
+ getContext().getPackageManager());
+
+ List groups = new ArrayList<>();
+ Set seenPermissions = new ArraySet<>();
+
+ PackageManager packageManager = getContext().getPackageManager();
+ List groupInfos = packageManager.getAllPermissionGroups(0);
+
+ for (PermissionGroupInfo groupInfo : groupInfos) {
+ // Mare sure we respond to cancellation.
+ if (isLoadInBackgroundCanceled()) {
+ return Collections.emptyList();
+ }
+
+ // Get the permissions in this group.
+ final List groupPermissions;
+ try {
+ groupPermissions = packageManager.queryPermissionsByGroup(groupInfo.name, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue;
+ }
+
+ boolean hasRuntimePermissions = false;
+
+ // Cache seen permissions and see if group has runtime permissions.
+ for (PermissionInfo groupPermission : groupPermissions) {
+ seenPermissions.add(groupPermission.name);
+ if ((groupPermission.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS
+ && (groupPermission.flags & PermissionInfo.FLAG_INSTALLED) != 0
+ && (groupPermission.flags & PermissionInfo.FLAG_REMOVED) == 0) {
+ hasRuntimePermissions = true;
+ }
+ }
+
+ // No runtime permissions - not interesting for us.
+ if (!hasRuntimePermissions) {
+ continue;
+ }
+
+ CharSequence label = loadItemInfoLabel(groupInfo);
+ Drawable icon = loadItemInfoIcon(groupInfo);
+
+ PermissionApps permApps = new PermissionApps(getContext(), groupInfo.name, null,
+ pmCache);
+ permApps.refreshSync();
+
+ // Create the group and add to the list.
+ PermissionGroup group = new PermissionGroup(groupInfo.name,
+ groupInfo.packageName, label, icon, permApps.getTotalCount(launcherPkgs),
+ permApps.getGrantedCount(launcherPkgs));
+ groups.add(group);
+ }
+
+
+ // Make sure we add groups for lone runtime permissions.
+ List installedPackages = getContext().getPackageManager()
+ .getInstalledPackages(PackageManager.GET_PERMISSIONS);
+
+
+ // We will filter out permissions that no package requests.
+ Set requestedPermissions = new ArraySet<>();
+ for (PackageInfo installedPackage : installedPackages) {
+ if (installedPackage.requestedPermissions == null) {
+ continue;
+ }
+ for (String requestedPermission : installedPackage.requestedPermissions) {
+ requestedPermissions.add(requestedPermission);
+ }
+ }
+
+ for (PackageInfo installedPackage : installedPackages) {
+ if (installedPackage.permissions == null) {
+ continue;
+ }
+
+ for (PermissionInfo permissionInfo : installedPackage.permissions) {
+ // If we have handled this permission, no more work to do.
+ if (!seenPermissions.add(permissionInfo.name)) {
+ continue;
+ }
+
+ // We care only about installed runtime permissions.
+ if ((permissionInfo.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ != PermissionInfo.PROTECTION_DANGEROUS
+ || (permissionInfo.flags & PermissionInfo.FLAG_INSTALLED) == 0) {
+ continue;
+ }
+
+ // If no app uses this permission,
+ if (!requestedPermissions.contains(permissionInfo.name)) {
+ continue;
+ }
+
+ CharSequence label = loadItemInfoLabel(permissionInfo);
+ Drawable icon = loadItemInfoIcon(permissionInfo);
+
+ PermissionApps permApps = new PermissionApps(getContext(), permissionInfo.name,
+ null, pmCache);
+ permApps.refreshSync();
+
+ // Create the group and add to the list.
+ PermissionGroup group = new PermissionGroup(permissionInfo.name,
+ permissionInfo.packageName, label, icon,
+ permApps.getTotalCount(launcherPkgs),
+ permApps.getGrantedCount(launcherPkgs));
+ groups.add(group);
+ }
+ }
+
+ Collections.sort(groups);
+ return groups;
+ }
+
+ private CharSequence loadItemInfoLabel(PackageItemInfo itemInfo) {
+ CharSequence label = itemInfo.loadSafeLabel(getContext().getPackageManager(), 0,
+ SAFE_LABEL_FLAG_FIRST_LINE | SAFE_LABEL_FLAG_TRIM);
+ if (label == null) {
+ label = itemInfo.name;
+ }
+ return label;
+ }
+
+ private Drawable loadItemInfoIcon(PackageItemInfo itemInfo) {
+ Drawable icon = null;
+ if (itemInfo.icon > 0) {
+ icon = Utils.loadDrawable(getContext().getPackageManager(),
+ itemInfo.packageName, itemInfo.icon);
+ }
+ if (icon == null) {
+ icon = getContext().getDrawable(R.drawable.ic_perm_device_info);
+ }
+ return icon;
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid) {
+ forceLoad();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/service/RuntimePermissionPresenterServiceImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/service/RuntimePermissionPresenterServiceImpl.java
new file mode 100644
index 0000000000000..ab19fa11a6136
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/service/RuntimePermissionPresenterServiceImpl.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.service;
+
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.permission.RuntimePermissionPresentationInfo;
+import android.permissionpresenterservice.RuntimePermissionPresenterService;
+import android.util.Log;
+
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Service that provides presentation information for runtime permissions.
+ */
+public final class RuntimePermissionPresenterServiceImpl extends RuntimePermissionPresenterService {
+ private static final String LOG_TAG = "PermissionPresenter";
+
+ @Override
+ public List onGetAppPermissions(String packageName) {
+ final PackageInfo packageInfo;
+ try {
+ packageInfo = getPackageManager().getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Error getting package:" + packageName, e);
+ return null;
+ }
+
+ List permissions = new ArrayList<>();
+
+ AppPermissions appPermissions = new AppPermissions(this, packageInfo, false, null);
+ for (AppPermissionGroup group : appPermissions.getPermissionGroups()) {
+ if (Utils.shouldShowPermission(group)) {
+ final boolean granted = group.areRuntimePermissionsGranted();
+ final boolean standard = Utils.OS_PKG.equals(group.getDeclaringPackage());
+ RuntimePermissionPresentationInfo permission =
+ new RuntimePermissionPresentationInfo(group.getLabel(),
+ granted, standard);
+ permissions.add(permission);
+ }
+ }
+
+ return permissions;
+ }
+
+ @Override
+ public void onRevokeRuntimePermission(String packageName, String permissionName) {
+ try {
+ final PackageInfo packageInfo = getPackageManager().getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ final AppPermissions appPermissions = new AppPermissions(this, packageInfo, false,
+ null);
+
+ final AppPermissionGroup appPermissionGroup = appPermissions.getGroupForPermission(
+ permissionName);
+
+ if (appPermissionGroup != null) {
+ appPermissionGroup.revokeRuntimePermissions(false);
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(LOG_TAG, "Error getting package:" + packageName, e);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ButtonBarLayout.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ButtonBarLayout.java
new file mode 100644
index 0000000000000..8b0f219b9bf44
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ButtonBarLayout.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.View;
+import android.widget.LinearLayout;
+
+import com.android.packageinstaller.R;
+
+/**
+ * An extension of LinearLayout that automatically switches to vertical
+ * orientation when it can't fit its child views horizontally.
+ */
+public class ButtonBarLayout extends LinearLayout {
+ /** Whether the current configuration allows stacking. */
+ private boolean mAllowStacking;
+
+ private int mLastWidthSize = -1;
+
+ public ButtonBarLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ mAllowStacking = true;
+ }
+
+ public void setAllowStacking(boolean allowStacking) {
+ if (mAllowStacking != allowStacking) {
+ mAllowStacking = allowStacking;
+ if (!mAllowStacking && getOrientation() == LinearLayout.VERTICAL) {
+ setStacked(false);
+ }
+ requestLayout();
+ }
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+
+ if (mAllowStacking) {
+ if (widthSize > mLastWidthSize && isStacked()) {
+ // We're being measured wider this time, try un-stacking.
+ setStacked(false);
+ }
+
+ mLastWidthSize = widthSize;
+ }
+
+ boolean needsRemeasure = false;
+
+ // If we're not stacked, make sure the measure spec is AT_MOST rather
+ // than EXACTLY. This ensures that we'll still get TOO_SMALL so that we
+ // know to stack the buttons.
+ final int initialWidthMeasureSpec;
+ if (!isStacked() && MeasureSpec.getMode(widthMeasureSpec) == MeasureSpec.EXACTLY) {
+ initialWidthMeasureSpec = MeasureSpec.makeMeasureSpec(widthSize, MeasureSpec.AT_MOST);
+
+ // We'll need to remeasure again to fill excess space.
+ needsRemeasure = true;
+ } else {
+ initialWidthMeasureSpec = widthMeasureSpec;
+ }
+
+ super.onMeasure(initialWidthMeasureSpec, heightMeasureSpec);
+
+ if (mAllowStacking && !isStacked()) {
+ final int measuredWidth = getMeasuredWidthAndState();
+ final int measuredWidthState = measuredWidth & MEASURED_STATE_MASK;
+ if (measuredWidthState == MEASURED_STATE_TOO_SMALL) {
+ setStacked(true);
+
+ // Measure again in the new orientation.
+ needsRemeasure = true;
+ }
+ }
+
+ if (needsRemeasure) {
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+ }
+
+ private void setStacked(boolean stacked) {
+ setOrientation(stacked ? LinearLayout.VERTICAL : LinearLayout.HORIZONTAL);
+ setGravity(stacked ? Gravity.END : Gravity.BOTTOM);
+
+ final View spacer = findViewById(R.id.spacer);
+ if (spacer != null) {
+ spacer.setVisibility(stacked ? View.GONE : View.INVISIBLE);
+ }
+ }
+
+ private boolean isStacked() {
+ return getOrientation() == LinearLayout.VERTICAL;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java
new file mode 100644
index 0000000000000..c13f63098230e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ConfirmActionDialogFragment.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.os.Bundle;
+
+import androidx.fragment.app.DialogFragment;
+
+import com.android.packageinstaller.R;
+
+public final class ConfirmActionDialogFragment extends DialogFragment {
+ public static final String ARG_MESSAGE = "MESSAGE";
+ public static final String ARG_ACTION = "ACTION";
+
+ public interface OnActionConfirmedListener {
+ void onActionConfirmed(String action);
+ }
+
+ public static ConfirmActionDialogFragment newInstance(CharSequence message, String action) {
+ Bundle arguments = new Bundle();
+ arguments.putCharSequence(ARG_MESSAGE, message);
+ arguments.putString(ARG_ACTION, action);
+ ConfirmActionDialogFragment fragment = new ConfirmActionDialogFragment();
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public Dialog onCreateDialog(Bundle bundle) {
+ return new AlertDialog.Builder(getContext())
+ .setMessage(getArguments().getString(ARG_MESSAGE))
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
+ (dialog, which) -> {
+ Activity activity = getActivity();
+ if (activity instanceof OnActionConfirmedListener) {
+ String groupName = getArguments().getString(ARG_ACTION);
+ ((OnActionConfirmedListener) activity)
+ .onActionConfirmed(groupName);
+ }
+ })
+ .create();
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
new file mode 100644
index 0000000000000..51bd2836a2822
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsActivity.java
@@ -0,0 +1,834 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import static android.content.pm.PackageManager.PERMISSION_DENIED;
+
+import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.DENIED;
+import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler
+ .DENIED_DO_NOT_ASK_AGAIN;
+import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler.GRANTED_ALWAYS;
+import static com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler
+ .GRANTED_FOREGROUND_ONLY;
+import static com.android.packageinstaller.permission.utils.Utils.getRequestMessage;
+
+import android.app.admin.DevicePolicyManager;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PackageParser;
+import android.content.res.Resources;
+import android.graphics.drawable.Icon;
+import android.os.Build;
+import android.os.Bundle;
+import android.text.Html;
+import android.text.Spanned;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.Window;
+import android.view.WindowManager;
+
+import com.android.internal.content.PackageMonitor;
+import com.android.internal.logging.nano.MetricsProto;
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.model.Permission;
+import com.android.packageinstaller.permission.ui.auto.GrantPermissionsAutoViewHandler;
+import com.android.packageinstaller.permission.utils.ArrayUtils;
+import com.android.packageinstaller.permission.utils.EventLogger;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+public class GrantPermissionsActivity extends OverlayTouchActivity
+ implements GrantPermissionsViewHandler.ResultListener {
+
+ private static final String LOG_TAG = "GrantPermissionsActivity";
+
+ private String[] mRequestedPermissions;
+
+ private ArrayMap, GroupState> mRequestGrantPermissionGroups =
+ new ArrayMap<>();
+
+ private GrantPermissionsViewHandler mViewHandler;
+ private AppPermissions mAppPermissions;
+
+ boolean mResultSet;
+
+ private PackageManager.OnPermissionsChangedListener mPermissionChangeListener;
+ private PackageMonitor mPackageMonitor;
+
+ private String mCallingPackage;
+
+ private int getPermissionPolicy() {
+ DevicePolicyManager devicePolicyManager = getSystemService(DevicePolicyManager.class);
+ return devicePolicyManager.getPermissionPolicy(null);
+ }
+
+ /**
+ * Try to add a single permission that is requested to be granted.
+ *
+ *
This does not expand the permissions into the {@link #computeAffectedPermissions
+ * affected permissions}.
+ *
+ * @param group The group the permission belongs to (might be a background permission group)
+ * @param permission The permission to add
+ * @param isFirstInstance Is this the first time the groupStates get created
+ */
+ private void addRequestedPermissions(AppPermissionGroup group, String permission,
+ boolean isFirstInstance) {
+ if (!group.isGrantingAllowed()) {
+ // Skip showing groups that we know cannot be granted.
+ return;
+ }
+
+ // We allow the user to choose only non-fixed permissions. A permission
+ // is fixed either by device policy or the user denying with prejudice.
+ if (!group.isUserFixed() && !group.isPolicyFixed()) {
+ Pair groupKey = new Pair<>(group.getName(),
+ group.isBackgroundGroup());
+
+ GroupState state = mRequestGrantPermissionGroups.get(groupKey);
+ if (state == null) {
+ state = new GroupState(group);
+ mRequestGrantPermissionGroups.put(groupKey, state);
+ }
+ state.affectedPermissions = ArrayUtils.appendString(
+ state.affectedPermissions, permission);
+
+ boolean skipGroup = false;
+ switch (getPermissionPolicy()) {
+ case DevicePolicyManager.PERMISSION_POLICY_AUTO_GRANT: {
+ if (!group.areRuntimePermissionsGranted()) {
+ group.grantRuntimePermissions(false, new String[]{permission});
+ }
+ state.mState = GroupState.STATE_ALLOWED;
+ group.setPolicyFixed();
+ skipGroup = true;
+ } break;
+
+ case DevicePolicyManager.PERMISSION_POLICY_AUTO_DENY: {
+ if (group.areRuntimePermissionsGranted()) {
+ group.revokeRuntimePermissions(false, new String[]{permission});
+ }
+ state.mState = GroupState.STATE_DENIED;
+ group.setPolicyFixed();
+ skipGroup = true;
+ } break;
+
+ default: {
+ if (group.areRuntimePermissionsGranted()) {
+ group.grantRuntimePermissions(false, new String[]{permission});
+ state.mState = GroupState.STATE_ALLOWED;
+ skipGroup = true;
+ }
+ } break;
+ }
+
+ if (skipGroup && isFirstInstance) {
+ // Only allow to skip groups when this is the first time the dialog was created.
+ // Otherwise the number of groups changes between instances of the dialog.
+ state.mState = GroupState.STATE_SKIPPED;
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ // Cache this as this can only read on onCreate, not later.
+ mCallingPackage = getCallingPackage();
+
+ mPackageMonitor = new PackageMonitor() {
+ @Override
+ public void onPackageRemoved(String packageName, int uid) {
+ if (mCallingPackage.equals(packageName)) {
+ Log.w(LOG_TAG, mCallingPackage + " was uninstalled");
+
+ finish();
+ }
+ }
+ };
+
+ setFinishOnTouchOutside(false);
+
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED);
+ getWindow().addFlags(WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON);
+
+ setTitle(R.string.permission_request_title);
+
+ if (DeviceUtils.isTelevision(this)) {
+ mViewHandler = new com.android.packageinstaller.permission.ui.television
+ .GrantPermissionsViewHandlerImpl(this,
+ mCallingPackage).setResultListener(this);
+ } else if (DeviceUtils.isWear(this)) {
+ mViewHandler = new GrantPermissionsWatchViewHandler(this).setResultListener(this);
+ } else if (DeviceUtils.isAuto(this)) {
+ mViewHandler = new GrantPermissionsAutoViewHandler(this, mCallingPackage)
+ .setResultListener(this);
+ } else {
+ mViewHandler = new com.android.packageinstaller.permission.ui.handheld
+ .GrantPermissionsViewHandlerImpl(this, mCallingPackage)
+ .setResultListener(this);
+ }
+
+ mRequestedPermissions = getIntent().getStringArrayExtra(
+ PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES);
+ if (mRequestedPermissions == null) {
+ mRequestedPermissions = new String[0];
+ }
+
+ final int requestedPermCount = mRequestedPermissions.length;
+
+ if (requestedPermCount == 0) {
+ setResultAndFinish();
+ return;
+ }
+
+ try {
+ mPermissionChangeListener = new PermissionChangeListener();
+ } catch (NameNotFoundException e) {
+ setResultAndFinish();
+ return;
+ }
+
+ PackageInfo callingPackageInfo = getCallingPackageInfo();
+
+ if (callingPackageInfo == null || callingPackageInfo.requestedPermissions == null
+ || callingPackageInfo.requestedPermissions.length <= 0) {
+ setResultAndFinish();
+ return;
+ }
+
+ // Don't allow legacy apps to request runtime permissions.
+ if (callingPackageInfo.applicationInfo.targetSdkVersion < Build.VERSION_CODES.M) {
+ // Returning empty arrays means a cancellation.
+ mRequestedPermissions = new String[0];
+ setResultAndFinish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(this, callingPackageInfo, false,
+ new Runnable() {
+ @Override
+ public void run() {
+ setResultAndFinish();
+ }
+ });
+
+ for (String requestedPermission : mRequestedPermissions) {
+ if (requestedPermission == null) {
+ continue;
+ }
+
+ ArrayList affectedPermissions =
+ computeAffectedPermissions(requestedPermission);
+
+ int numAffectedPermissions = affectedPermissions.size();
+ for (int i = 0; i < numAffectedPermissions; i++) {
+ AppPermissionGroup group =
+ mAppPermissions.getGroupForPermission(affectedPermissions.get(i));
+ if (group == null) {
+ continue;
+ }
+
+ addRequestedPermissions(group, affectedPermissions.get(i), icicle == null);
+ }
+ }
+
+ int numGroupStates = mRequestGrantPermissionGroups.size();
+ for (int groupStateNum = 0; groupStateNum < numGroupStates; groupStateNum++) {
+ GroupState groupState = mRequestGrantPermissionGroups.valueAt(groupStateNum);
+ AppPermissionGroup group = groupState.mGroup;
+
+ // Restore permission group state after lifecycle events
+ if (icicle != null) {
+ groupState.mState = icicle.getInt(
+ getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(groupStateNum)),
+ groupState.mState);
+ }
+
+ // Do not attempt to grant background access if foreground access is not either already
+ // granted or requested
+ if (group.isBackgroundGroup()) {
+ // Check if a foreground permission is already granted
+ boolean foregroundGroupAlreadyGranted = mAppPermissions.getPermissionGroup(
+ group.getName()).areRuntimePermissionsGranted();
+ boolean hasForegroundRequest = (getForegroundGroupState(group.getName()) != null);
+
+ if (!foregroundGroupAlreadyGranted && !hasForegroundRequest) {
+ // The background permission cannot be granted at this time
+ int numPermissions = groupState.affectedPermissions.length;
+ for (int permissionNum = 0; permissionNum < numPermissions; permissionNum++) {
+ Log.w(LOG_TAG,
+ "Cannot grant " + groupState.affectedPermissions[permissionNum]
+ + " as the matching foreground permission is not already "
+ + "granted.");
+ }
+
+ groupState.mState = GroupState.STATE_SKIPPED;
+ }
+ }
+ }
+
+ setContentView(mViewHandler.createView());
+
+ Window window = getWindow();
+ WindowManager.LayoutParams layoutParams = window.getAttributes();
+ mViewHandler.updateWindowAttributes(layoutParams);
+ window.setAttributes(layoutParams);
+
+ // Restore UI state after lifecycle events. This has to be before
+ // showNextPermissionGroupGrantRequest is called. showNextPermissionGroupGrantRequest might
+ // update the UI and the UI behaves differently for updates and initial creations.
+ if (icicle != null) {
+ mViewHandler.loadInstanceState(icicle);
+ }
+
+ if (!showNextPermissionGroupGrantRequest()) {
+ setResultAndFinish();
+ } else if (icicle == null) {
+ int numRequestedPermissions = mRequestedPermissions.length;
+ for (int permissionNum = 0; permissionNum < numRequestedPermissions; permissionNum++) {
+ String permission = mRequestedPermissions[permissionNum];
+
+ EventLogger.logPermission(
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_REQUESTED, permission,
+ mAppPermissions.getPackageInfo().packageName);
+ }
+ }
+ }
+
+ /**
+ * Update the {@link #mRequestedPermissions} if the system reports them as granted.
+ *
+ *
This also updates the {@link #mAppPermissions} state and switches to the next group grant
+ * request if the current group becomes granted.
+ */
+ private void updateIfPermissionsWereGranted() {
+ PackageManager pm = getPackageManager();
+
+ boolean mightShowNextGroup = true;
+ int numGroupStates = mRequestGrantPermissionGroups.size();
+ for (int i = 0; i < numGroupStates; i++) {
+ GroupState groupState = mRequestGrantPermissionGroups.valueAt(i);
+
+ if (groupState == null || groupState.mState != GroupState.STATE_UNKNOWN) {
+ // Group has already been approved / denied via the UI by the user
+ continue;
+ }
+
+ boolean allAffectedPermissionsOfThisGroupAreGranted = true;
+
+ if (groupState.affectedPermissions == null) {
+ // It is not clear which permissions belong to this group, hence never skip this
+ // view
+ allAffectedPermissionsOfThisGroupAreGranted = false;
+ } else {
+ for (int permNum = 0; permNum < groupState.affectedPermissions.length;
+ permNum++) {
+ if (pm.checkPermission(groupState.affectedPermissions[permNum], mCallingPackage)
+ == PERMISSION_DENIED) {
+ allAffectedPermissionsOfThisGroupAreGranted = false;
+ break;
+ }
+ }
+ }
+
+ if (allAffectedPermissionsOfThisGroupAreGranted) {
+ groupState.mState = GroupState.STATE_ALLOWED;
+
+ if (mightShowNextGroup) {
+ // The UI currently displays the first group with
+ // mState == STATE_UNKNOWN. So we are switching to next group until we
+ // could not allow a group that was still unknown
+ if (!showNextPermissionGroupGrantRequest()) {
+ setResultAndFinish();
+ }
+ }
+ } else {
+ mightShowNextGroup = false;
+ }
+ }
+ }
+
+ @Override
+ protected void onStart() {
+ super.onStart();
+
+ PackageManager pm = getPackageManager();
+ pm.addOnPermissionsChangeListener(mPermissionChangeListener);
+
+ // get notified when the package is removed
+ mPackageMonitor.register(this, getMainLooper(), false);
+
+ // check if the package was removed while this activity was not started
+ try {
+ pm.getPackageInfo(mCallingPackage, 0);
+ } catch (NameNotFoundException e) {
+ Log.w(LOG_TAG, mCallingPackage + " was uninstalled while this activity was stopped", e);
+ finish();
+ }
+
+ updateIfPermissionsWereGranted();
+ }
+
+ @Override
+ protected void onStop() {
+ super.onStop();
+
+ mPackageMonitor.unregister();
+
+ getPackageManager().removeOnPermissionsChangeListener(mPermissionChangeListener);
+ }
+
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent ev) {
+ View rootView = getWindow().getDecorView();
+ if (rootView.getTop() != 0) {
+ // We are animating the top view, need to compensate for that in motion events.
+ ev.setLocation(ev.getX(), ev.getY() - rootView.getTop());
+ }
+ return super.dispatchTouchEvent(ev);
+ }
+
+ /**
+ * Compose a key that stores the GroupState.mState in the instance state.
+ *
+ * @param requestGrantPermissionGroupsKey The key of the permission group
+ *
+ * @return A unique key to be used in the instance state
+ */
+ private static String getInstanceStateKey(
+ Pair requestGrantPermissionGroupsKey) {
+ return GrantPermissionsActivity.class.getName() + "_"
+ + requestGrantPermissionGroupsKey.first + "_"
+ + requestGrantPermissionGroupsKey.second;
+ }
+
+ @Override
+ protected void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ mViewHandler.saveInstanceState(outState);
+
+ int numGroups = mRequestGrantPermissionGroups.size();
+ for (int i = 0; i < numGroups; i++) {
+ int state = mRequestGrantPermissionGroups.valueAt(i).mState;
+
+ if (state != GroupState.STATE_UNKNOWN) {
+ outState.putInt(getInstanceStateKey(mRequestGrantPermissionGroups.keyAt(i)), state);
+ }
+ }
+ }
+
+ /**
+ * @return the background group state for the permission group with the {@code name}
+ */
+ private GroupState getBackgroundGroupState(String name) {
+ return mRequestGrantPermissionGroups.get(new Pair<>(name, true));
+ }
+
+ /**
+ * @return the foreground group state for the permission group with the {@code name}
+ */
+ private GroupState getForegroundGroupState(String name) {
+ return mRequestGrantPermissionGroups.get(new Pair<>(name, false));
+ }
+
+ private boolean shouldShowRequestForGroupState(GroupState groupState) {
+ if (groupState.mState == GroupState.STATE_SKIPPED) {
+ return false;
+ }
+
+ GroupState foregroundGroup = getForegroundGroupState(groupState.mGroup.getName());
+ if (groupState.mGroup.isBackgroundGroup()
+ && (foregroundGroup != null && shouldShowRequestForGroupState(foregroundGroup))) {
+ // If an app requests both foreground and background permissions of the same group,
+ // we only show one request
+ return false;
+ }
+
+ return true;
+ }
+
+ private boolean showNextPermissionGroupGrantRequest() {
+ int numGroupStates = mRequestGrantPermissionGroups.size();
+ int numGrantRequests = 0;
+ for (int i = 0; i < numGroupStates; i++) {
+ if (shouldShowRequestForGroupState(mRequestGrantPermissionGroups.valueAt(i))) {
+ numGrantRequests++;
+ }
+ }
+
+ int currentIndex = 0;
+ for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
+ if (!shouldShowRequestForGroupState(groupState)) {
+ continue;
+ }
+
+ if (groupState.mState == GroupState.STATE_UNKNOWN) {
+ GroupState foregroundGroupState;
+ GroupState backgroundGroupState;
+ if (groupState.mGroup.isBackgroundGroup()) {
+ backgroundGroupState = groupState;
+ foregroundGroupState = getForegroundGroupState(groupState.mGroup.getName());
+ } else {
+ foregroundGroupState = groupState;
+ backgroundGroupState = getBackgroundGroupState(groupState.mGroup.getName());
+ }
+
+ CharSequence appLabel = mAppPermissions.getAppLabel();
+
+ // Set the new grant view
+ // TODO: Use a real message for the action. We need group action APIs
+ Resources resources;
+ try {
+ resources = getPackageManager().getResourcesForApplication(
+ groupState.mGroup.getIconPkg());
+ } catch (NameNotFoundException e) {
+ // Fallback to system.
+ resources = Resources.getSystem();
+ }
+
+ Icon icon;
+ try {
+ icon = Icon.createWithResource(resources, groupState.mGroup.getIconResId());
+ } catch (Resources.NotFoundException e) {
+ Log.e(LOG_TAG, "Cannot load icon for group" + groupState.mGroup.getName(), e);
+ icon = null;
+ }
+
+ // If no background permissions are granted yet, we need to ask for background
+ // permissions
+ boolean needBackgroundPermission = false;
+ boolean isBackgroundPermissionUserSet = false;
+ if (backgroundGroupState != null) {
+ if (!backgroundGroupState.mGroup.areRuntimePermissionsGranted()) {
+ needBackgroundPermission = true;
+ isBackgroundPermissionUserSet = backgroundGroupState.mGroup.isUserSet();
+ }
+ }
+
+ // If no foreground permissions are granted yet, we need to ask for foreground
+ // permissions
+ boolean needForegroundPermission = false;
+ boolean isForegroundPermissionUserSet = false;
+ if (foregroundGroupState != null) {
+ if (!foregroundGroupState.mGroup.areRuntimePermissionsGranted()) {
+ needForegroundPermission = true;
+ isForegroundPermissionUserSet = foregroundGroupState.mGroup.isUserSet();
+ }
+ }
+
+ boolean showForegroundChooser = false;
+ int messageId;
+ int detailMessageId = 0;
+ if (needForegroundPermission) {
+ messageId = groupState.mGroup.getRequest();
+
+ if (needBackgroundPermission) {
+ showForegroundChooser = true;
+ } else {
+ if (foregroundGroupState.mGroup.hasPermissionWithBackgroundMode()) {
+ detailMessageId = groupState.mGroup.getRequestDetail();
+ }
+ }
+ } else {
+ if (needBackgroundPermission) {
+ messageId = groupState.mGroup.getBackgroundRequest();
+ detailMessageId = groupState.mGroup.getBackgroundRequestDetail();
+ } else {
+ // Not reached as the permissions should be auto-granted
+ return false;
+ }
+ }
+
+ CharSequence message = getRequestMessage(appLabel, groupState.mGroup, this,
+ messageId);
+
+ Spanned detailMessage = null;
+ if (detailMessageId != 0) {
+ try {
+ detailMessage = Html.fromHtml(
+ getPackageManager().getResourcesForApplication(
+ groupState.mGroup.getDeclaringPackage()).getString(
+ detailMessageId), 0);
+ } catch (NameNotFoundException ignored) {
+ }
+ }
+
+ // Set the permission message as the title so it can be announced.
+ setTitle(message);
+
+ mViewHandler.updateUi(groupState.mGroup.getName(), numGrantRequests, currentIndex,
+ icon, message, detailMessage, showForegroundChooser,
+ isForegroundPermissionUserSet || isBackgroundPermissionUserSet);
+
+ return true;
+ }
+
+ if (groupState.mState != GroupState.STATE_SKIPPED) {
+ currentIndex++;
+ }
+ }
+
+ return false;
+ }
+
+ @Override
+ public void onPermissionGrantResult(String name,
+ @GrantPermissionsViewHandler.Result int result) {
+ GroupState foregroundGroupState = getForegroundGroupState(name);
+ GroupState backgroundGroupState = getBackgroundGroupState(name);
+
+ switch (result) {
+ case GRANTED_ALWAYS :
+ if (foregroundGroupState != null) {
+ onPermissionGrantResultSingleState(foregroundGroupState, true, false);
+ }
+ if (backgroundGroupState != null) {
+ onPermissionGrantResultSingleState(backgroundGroupState, true, false);
+ }
+ break;
+ case GRANTED_FOREGROUND_ONLY :
+ if (foregroundGroupState != null) {
+ onPermissionGrantResultSingleState(foregroundGroupState, true, false);
+ }
+ if (backgroundGroupState != null) {
+ onPermissionGrantResultSingleState(backgroundGroupState, false, false);
+ }
+ break;
+ case DENIED :
+ if (foregroundGroupState != null) {
+ onPermissionGrantResultSingleState(foregroundGroupState, false, false);
+ }
+ if (backgroundGroupState != null) {
+ onPermissionGrantResultSingleState(backgroundGroupState, false, false);
+ }
+ break;
+ case DENIED_DO_NOT_ASK_AGAIN :
+ if (foregroundGroupState != null) {
+ onPermissionGrantResultSingleState(foregroundGroupState, false, true);
+ }
+ if (backgroundGroupState != null) {
+ onPermissionGrantResultSingleState(backgroundGroupState, false, true);
+ }
+ break;
+ }
+
+ if (!showNextPermissionGroupGrantRequest()) {
+ setResultAndFinish();
+ }
+ }
+
+ /**
+ * Grants or revoked the affected permissions for a single {@link groupState}.
+ *
+ * @param groupState The group state with the permissions to grant/revoke
+ * @param granted {@code true} if the permissions should be granted, {@code false} if they
+ * should be revoked
+ * @param doNotAskAgain if the permissions should be revoked should be app be allowed to ask
+ * again for the same permissions?
+ */
+ private void onPermissionGrantResultSingleState(GroupState groupState, boolean granted,
+ boolean doNotAskAgain) {
+ if (groupState != null && groupState.mGroup != null) {
+ if (granted) {
+ groupState.mGroup.grantRuntimePermissions(doNotAskAgain,
+ groupState.affectedPermissions);
+ groupState.mState = GroupState.STATE_ALLOWED;
+ } else {
+ groupState.mGroup.revokeRuntimePermissions(doNotAskAgain,
+ groupState.affectedPermissions);
+ groupState.mState = GroupState.STATE_DENIED;
+
+ int numRequestedPermissions = mRequestedPermissions.length;
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ String permission = mRequestedPermissions[i];
+
+ if (groupState.mGroup.hasPermission(permission)) {
+ EventLogger.logPermission(
+ MetricsProto.MetricsEvent.ACTION_PERMISSION_DENIED, permission,
+ mAppPermissions.getPackageInfo().packageName);
+ }
+ }
+ }
+ }
+ }
+
+ @Override
+ public boolean onKeyDown(int keyCode, KeyEvent event) {
+ // We do not allow backing out.
+ return keyCode == KeyEvent.KEYCODE_BACK;
+ }
+
+ @Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // We do not allow backing out.
+ return keyCode == KeyEvent.KEYCODE_BACK;
+ }
+
+ @Override
+ public void finish() {
+ setResultIfNeeded(RESULT_CANCELED);
+ super.finish();
+ }
+
+ private PackageInfo getCallingPackageInfo() {
+ try {
+ return getPackageManager().getPackageInfo(mCallingPackage,
+ PackageManager.GET_PERMISSIONS);
+ } catch (NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package: " + mCallingPackage, e);
+ return null;
+ }
+ }
+
+ private void setResultIfNeeded(int resultCode) {
+ if (!mResultSet) {
+ mResultSet = true;
+ logRequestedPermissionGroups();
+ Intent result = new Intent(PackageManager.ACTION_REQUEST_PERMISSIONS);
+ result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_NAMES, mRequestedPermissions);
+
+ PackageManager pm = getPackageManager();
+ int numRequestedPermissions = mRequestedPermissions.length;
+ int[] grantResults = new int[numRequestedPermissions];
+ for (int i = 0; i < numRequestedPermissions; i++) {
+ grantResults[i] = pm.checkPermission(mRequestedPermissions[i], mCallingPackage);
+ }
+
+ result.putExtra(PackageManager.EXTRA_REQUEST_PERMISSIONS_RESULTS, grantResults);
+ setResult(resultCode, result);
+ }
+ }
+
+ private void setResultAndFinish() {
+ setResultIfNeeded(RESULT_OK);
+ finish();
+ }
+
+ private void logRequestedPermissionGroups() {
+ if (mRequestGrantPermissionGroups.isEmpty()) {
+ return;
+ }
+
+ final int groupCount = mRequestGrantPermissionGroups.size();
+ List groups = new ArrayList<>(groupCount);
+ for (GroupState groupState : mRequestGrantPermissionGroups.values()) {
+ groups.add(groupState.mGroup);
+ }
+
+ SafetyNetLogger.logPermissionsRequested(mAppPermissions.getPackageInfo(), groups);
+ }
+
+ /**
+ * Get the actually requested permissions when a permission is requested.
+ *
+ *
>In some cases requesting to grant a single permission requires the system to grant
+ * additional permissions. E.g. before N-MR1 a single permission of a group caused the whole
+ * group to be granted. Another case are permissions that are split into two. For apps that
+ * target an SDK before the split, this method automatically adds the split off permission.
+ *
+ * @param permission The requested permission
+ *
+ * @return The actually requested permissions
+ */
+ private ArrayList computeAffectedPermissions(String permission) {
+ int requestingAppTargetSDK =
+ mAppPermissions.getPackageInfo().applicationInfo.targetSdkVersion;
+
+ // If a permission is split, all permissions the original permission is split into are
+ // affected
+ ArrayList splitPerms = new ArrayList<>();
+ splitPerms.add(permission);
+ for (PackageParser.SplitPermissionInfo splitPerm : PackageParser.SPLIT_PERMISSIONS) {
+ if (requestingAppTargetSDK < splitPerm.targetSdk
+ && permission.equals(splitPerm.rootPerm)) {
+ Collections.addAll(splitPerms, splitPerm.newPerms);
+ }
+ }
+
+ // For <= N_MR1 apps all permissions of the groups of the requested permissions are affected
+ if (requestingAppTargetSDK <= Build.VERSION_CODES.N_MR1) {
+ ArrayList extendedPermissions = new ArrayList<>();
+
+ int numSplitPerms = splitPerms.size();
+ for (int splitPermNum = 0; splitPermNum < numSplitPerms; splitPermNum++) {
+ AppPermissionGroup group = mAppPermissions.getGroupForPermission(
+ splitPerms.get(splitPermNum));
+
+ if (group == null) {
+ continue;
+ }
+
+ ArrayList permissionsInGroup = group.getPermissions();
+ int numPermissionsInGroup = permissionsInGroup.size();
+ for (int permNum = 0; permNum < numPermissionsInGroup; permNum++) {
+ extendedPermissions.add(permissionsInGroup.get(permNum).getName());
+ }
+ }
+
+ return extendedPermissions;
+ } else {
+ return splitPerms;
+ }
+ }
+
+ private static final class GroupState {
+ static final int STATE_UNKNOWN = 0;
+ static final int STATE_ALLOWED = 1;
+ static final int STATE_DENIED = 2;
+ static final int STATE_SKIPPED = 3;
+
+ final AppPermissionGroup mGroup;
+ int mState = STATE_UNKNOWN;
+
+ /** Permissions of this group that need to be granted, null == no permissions of group */
+ String[] affectedPermissions;
+
+ GroupState(AppPermissionGroup group) {
+ mGroup = group;
+ }
+ }
+
+ private class PermissionChangeListener implements PackageManager.OnPermissionsChangedListener {
+ final int mCallingPackageUid;
+
+ PermissionChangeListener() throws NameNotFoundException {
+ mCallingPackageUid = getPackageManager().getPackageUid(mCallingPackage, 0);
+ }
+
+ @Override
+ public void onPermissionsChanged(int uid) {
+ if (uid == mCallingPackageUid) {
+ updateIfPermissionsWereGranted();
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsViewHandler.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsViewHandler.java
new file mode 100644
index 0000000000000..3f637f0f2334e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsViewHandler.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.view.View;
+import android.view.WindowManager;
+
+import androidx.annotation.IntDef;
+
+import java.lang.annotation.Retention;
+
+/**
+ * Class for managing the presentation and user interaction of the "grant
+ * permissions" user interface.
+ */
+public interface GrantPermissionsViewHandler {
+ @Retention(SOURCE)
+ @IntDef({GRANTED_ALWAYS, GRANTED_FOREGROUND_ONLY, DENIED, DENIED_DO_NOT_ASK_AGAIN})
+ @interface Result {}
+ int GRANTED_ALWAYS = 0;
+ int GRANTED_FOREGROUND_ONLY = 1;
+ int DENIED = 2;
+ int DENIED_DO_NOT_ASK_AGAIN = 3;
+
+ /**
+ * Listener interface for getting notified when the user responds to a
+ * permissions grant request.
+ */
+ interface ResultListener {
+ void onPermissionGrantResult(String groupName, @Result int result);
+ }
+
+ /**
+ * Creates and returns the view hierarchy that is managed by this view
+ * handler. This must be called before {@link #updateUi}.
+ */
+ View createView();
+
+ /**
+ * Updates the layout attributes of the current window. This is an optional
+ * operation; implementations only need to do work in this method if they
+ * need to alter the default styles provided by the activity's theme.
+ */
+ void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams);
+
+ /**
+ * Updates the view hierarchy to reflect the specified state.
+ *
+ * Note that this must be called at least once before showing the UI to
+ * the user to properly initialize the UI.
+ *
+ * @param groupName the name of the permission group
+ * @param groupCount the total number of groups that are being requested
+ * @param groupIndex the index of the current group being requested
+ * @param icon the icon representation of the current group
+ * @param message the message to display the user
+ * @param detailMessage another message to display to the user. This clarifies "message" in more
+ * detail
+ * @param showForegroundChooser whether to show the "only in foreground / always" option
+ * @param showDoNotAsk whether to show the "do not ask again" option
+ */
+ void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
+ CharSequence message, CharSequence detailMessage, boolean showForegroundChooser,
+ boolean showDoNotAsk);
+
+ /**
+ * Sets the result listener that will be notified when the user responds
+ * to a permissions grant request.
+ */
+ GrantPermissionsViewHandler setResultListener(ResultListener listener);
+
+ /**
+ * Called by {@link GrantPermissionsActivity} to save the state of this
+ * view handler to the specified bundle.
+ */
+ void saveInstanceState(Bundle outState);
+
+ /**
+ * Called by {@link GrantPermissionsActivity} to load the state of this
+ * view handler from the specified bundle.
+ */
+ void loadInstanceState(Bundle savedInstanceState);
+
+ /**
+ * Gives a chance for handling the back key.
+ */
+ void onBackPressed();
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java
new file mode 100644
index 0000000000000..f2251c1f3e7f0
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/GrantPermissionsWatchViewHandler.java
@@ -0,0 +1,216 @@
+package com.android.packageinstaller.permission.ui;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.style.TextAppearanceSpan;
+import android.util.Log;
+import android.view.View;
+import android.view.WindowManager;
+import android.widget.Space;
+
+import androidx.wear.ble.view.AcceptDenyDialog;
+import androidx.wear.ble.view.WearableDialogHelper;
+
+import com.android.packageinstaller.R;
+
+/**
+ * Watch-specific view handler for the grant permissions activity.
+ */
+final class GrantPermissionsWatchViewHandler implements GrantPermissionsViewHandler,
+ DialogInterface.OnClickListener {
+ private static final String TAG = "GrantPermsWatchViewH";
+
+ private static final String WATCH_HANDLER_BUNDLE = "watch_handler_bundle";
+ private static final String DIALOG_BUNDLE = "dialog_bundle";
+ private static final String GROUP_NAME = "group_name";
+ private static final String SHOW_DO_NOT_ASK = "show_do_not_ask";
+ private static final String ICON = "icon";
+ private static final String MESSAGE = "message";
+ private static final String CURRENT_PAGE_TEXT = "current_page_text";
+
+ private final Context mContext;
+
+ private ResultListener mResultListener;
+
+ private Dialog mDialog;
+
+ private String mGroupName;
+ private boolean mShowDoNotAsk;
+
+ private CharSequence mMessage;
+ private String mCurrentPageText;
+ private Icon mIcon;
+
+ GrantPermissionsWatchViewHandler(Context context) {
+ mContext = context;
+ }
+
+ @Override
+ public GrantPermissionsWatchViewHandler setResultListener(ResultListener listener) {
+ mResultListener = listener;
+ return this;
+ }
+
+ @Override
+ public View createView() {
+ return new Space(mContext);
+ }
+
+ @Override
+ public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) {
+ outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ outLayoutParams.height = WindowManager.LayoutParams.MATCH_PARENT;
+ outLayoutParams.format = PixelFormat.OPAQUE;
+ outLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ outLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ }
+
+ @Override
+ public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
+ CharSequence message, CharSequence detailMessage, boolean showForegroundChooser,
+ boolean showDoNotAsk) {
+ // TODO: Handle detailMessage
+ // TODO: Handle showForegroundChooser
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "updateUi() - groupName: " + groupName
+ + ", groupCount: " + groupCount
+ + ", groupIndex: " + groupIndex
+ + ", icon: " + icon
+ + ", message: " + message
+ + ", showDoNotAsk: " + showDoNotAsk);
+ }
+
+ mGroupName = groupName;
+ mShowDoNotAsk = showDoNotAsk;
+ mMessage = message;
+ mIcon = icon;
+ mCurrentPageText = groupCount > 1
+ ? mContext.getString(R.string.current_permission_template,
+ groupIndex + 1, groupCount)
+ : null;
+ showDialog(null);
+ }
+
+ private void showDialog(Bundle savedInstanceState) {
+ TypedArray a = mContext.obtainStyledAttributes(
+ new int[] { android.R.attr.textColorPrimary });
+ int color = a.getColor(0, mContext.getColor(android.R.color.white));
+ a.recycle();
+ Drawable drawable = mIcon == null ? null : mIcon.setTint(color).loadDrawable(mContext);
+
+ SpannableStringBuilder ssb = new SpannableStringBuilder();
+ if (!TextUtils.isEmpty(mCurrentPageText)) {
+ ssb.append(mCurrentPageText, new TextAppearanceSpan(mContext, R.style.BreadcrumbText),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ ssb.append('\n');
+ }
+ if (!TextUtils.isEmpty(mMessage)) {
+ ssb.append(mMessage, new TextAppearanceSpan(mContext, R.style.TitleText),
+ Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ if (mDialog != null) {
+ mDialog.dismiss();
+ mDialog = null;
+ }
+
+ if (mShowDoNotAsk) {
+ AlertDialog alertDialog = new WearableDialogHelper.DialogBuilder(mContext)
+ .setPositiveIcon(R.drawable.confirm_button)
+ .setNeutralIcon(R.drawable.cancel_button)
+ .setNegativeIcon(R.drawable.deny_button)
+ .setTitle(ssb)
+ .setIcon(drawable)
+ .setPositiveButton(R.string.grant_dialog_button_allow, this)
+ .setNeutralButton(R.string.grant_dialog_button_deny, this)
+ .setNegativeButton(R.string.grant_dialog_button_deny_dont_ask_again, this)
+ .show();
+ alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)
+ .setId(R.id.permission_allow_button);
+ alertDialog.getButton(DialogInterface.BUTTON_NEUTRAL)
+ .setId(R.id.permission_deny_button);
+ alertDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
+ .setId(R.id.permission_deny_dont_ask_again_button);
+
+ mDialog = alertDialog;
+ } else {
+ AcceptDenyDialog acceptDenyDialog = new AcceptDenyDialog(mContext);
+ acceptDenyDialog.setTitle(ssb);
+ acceptDenyDialog.setIcon(drawable);
+ acceptDenyDialog.setPositiveButton(this);
+ acceptDenyDialog.setNegativeButton(this);
+ acceptDenyDialog.show();
+ acceptDenyDialog.getButton(DialogInterface.BUTTON_POSITIVE)
+ .setId(R.id.permission_allow_button);
+ acceptDenyDialog.getButton(DialogInterface.BUTTON_NEGATIVE)
+ .setId(R.id.permission_deny_button);
+
+ mDialog = acceptDenyDialog;
+ }
+ mDialog.setCancelable(false);
+
+ if (savedInstanceState != null) {
+ mDialog.onRestoreInstanceState(savedInstanceState);
+ }
+ }
+
+ @Override
+ public void saveInstanceState(Bundle outState) {
+ Bundle b = new Bundle();
+ b.putByte(SHOW_DO_NOT_ASK, (byte) (mShowDoNotAsk ? 1 : 0));
+ b.putString(GROUP_NAME, mGroupName);
+ b.putBundle(DIALOG_BUNDLE, mDialog.onSaveInstanceState());
+
+ outState.putBundle(WATCH_HANDLER_BUNDLE, b);
+ }
+
+ @Override
+ public void loadInstanceState(Bundle savedInstanceState) {
+ Bundle b = savedInstanceState.getBundle(WATCH_HANDLER_BUNDLE);
+ mShowDoNotAsk = b.getByte(SHOW_DO_NOT_ASK) == 1;
+ mGroupName = b.getString(GROUP_NAME);
+ showDialog(b.getBundle(DIALOG_BUNDLE));
+ }
+
+ @Override
+ public void onBackPressed() {
+ notifyListener(DENIED);
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ switch (which) {
+ case DialogInterface.BUTTON_POSITIVE:
+ notifyListener(GRANTED_ALWAYS);
+ break;
+ case DialogInterface.BUTTON_NEUTRAL:
+ notifyListener(DENIED);
+ break;
+ case DialogInterface.BUTTON_NEGATIVE:
+ /* In AlertDialog, the negative button is also a don't ask again button. */
+ if (dialog instanceof AlertDialog) {
+ notifyListener(DENIED_DO_NOT_ASK_AGAIN);
+ } else {
+ notifyListener(DENIED);
+ }
+ break;
+ }
+ }
+
+ private void notifyListener(@Result int result) {
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName, result);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManagePermissionsActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManagePermissionsActivity.java
new file mode 100644
index 0000000000000..712b860c50471
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManagePermissionsActivity.java
@@ -0,0 +1,128 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.MenuItem;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.ui.handheld.ManageStandardPermissionsFragment;
+import com.android.packageinstaller.permission.ui.wear.AppPermissionsFragmentWear;
+
+public final class ManagePermissionsActivity extends OverlayTouchActivity {
+ private static final String LOG_TAG = "ManagePermissionsActivity";
+
+ public static final String EXTRA_ALL_PERMISSIONS =
+ "com.android.packageinstaller.extra.ALL_PERMISSIONS";
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ if (DeviceUtils.isAuto(this)) {
+ setTheme(R.style.CarSettingTheme);
+ }
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ return;
+ }
+
+ Fragment fragment;
+ String action = getIntent().getAction();
+
+ switch (action) {
+ case Intent.ACTION_MANAGE_PERMISSIONS: {
+ if (DeviceUtils.isTelevision(this)) {
+ fragment = com.android.packageinstaller.permission.ui.television
+ .ManagePermissionsFragment.newInstance();
+ } else {
+ fragment = ManageStandardPermissionsFragment.newInstance();
+ }
+ } break;
+
+ case Intent.ACTION_MANAGE_APP_PERMISSIONS: {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ if (packageName == null) {
+ Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PACKAGE_NAME");
+ finish();
+ return;
+ }
+ if (DeviceUtils.isAuto(this)) {
+ fragment = com.android.packageinstaller.permission.ui.auto
+ .AppPermissionsFragment.newInstance(packageName);
+ } else if (DeviceUtils.isWear(this)) {
+ fragment = AppPermissionsFragmentWear.newInstance(packageName);
+ } else if (DeviceUtils.isTelevision(this)) {
+ fragment = com.android.packageinstaller.permission.ui.television
+ .AppPermissionsFragment.newInstance(packageName);
+ } else {
+ final boolean allPermissions = getIntent().getBooleanExtra(
+ EXTRA_ALL_PERMISSIONS, false);
+ if (allPermissions) {
+ fragment = com.android.packageinstaller.permission.ui.handheld
+ .AllAppPermissionsFragment.newInstance(packageName);
+ } else {
+ fragment = com.android.packageinstaller.permission.ui.handheld
+ .AppPermissionsFragment.newInstance(packageName);
+ }
+ }
+ } break;
+
+ case Intent.ACTION_MANAGE_PERMISSION_APPS: {
+ String permissionName = getIntent().getStringExtra(Intent.EXTRA_PERMISSION_NAME);
+ if (permissionName == null) {
+ Log.i(LOG_TAG, "Missing mandatory argument EXTRA_PERMISSION_NAME");
+ finish();
+ return;
+ }
+ if (DeviceUtils.isTelevision(this)) {
+ fragment = com.android.packageinstaller.permission.ui.television
+ .PermissionAppsFragment.newInstance(permissionName);
+ } else {
+ fragment = com.android.packageinstaller.permission.ui.handheld
+ .PermissionAppsFragment.newInstance(permissionName);
+ }
+ } break;
+
+ default: {
+ Log.w(LOG_TAG, "Unrecognized action " + action);
+ finish();
+ return;
+ }
+ }
+
+ getFragmentManager().beginTransaction().replace(android.R.id.content, fragment).commit();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ // in automotive mode, there's no system wide back button, so need to add that
+ if (DeviceUtils.isAuto(this)) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ onBackPressed();
+ return true;
+ default:
+ return super.onOptionsItemSelected(item);
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java
new file mode 100644
index 0000000000000..0b07c806e5e61
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ManualLayoutFrame.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+public class ManualLayoutFrame extends ViewGroup {
+ private int mContentBottom;
+ private int mWidth;
+
+ public ManualLayoutFrame(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public void onConfigurationChanged() {
+ mContentBottom = 0;
+ mWidth = 0;
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ if (mWidth != 0) {
+ int newWidth = mWidth;
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ switch (widthMode) {
+ case MeasureSpec.AT_MOST: {
+ newWidth = Math.min(mWidth, MeasureSpec.getSize(widthMeasureSpec));
+ } break;
+ case MeasureSpec.EXACTLY: {
+ newWidth = MeasureSpec.getSize(widthMeasureSpec);
+ } break;
+ }
+ if (newWidth != mWidth) {
+ mWidth = newWidth;
+ }
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(mWidth, MeasureSpec.EXACTLY);
+ }
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ if (mWidth == 0) {
+ mWidth = getMeasuredWidth();
+ }
+
+ measureChild(getChildAt(0), widthMeasureSpec, heightMeasureSpec);
+ }
+
+ @Override
+ protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+ // We want to keep the content bottom at the same place to avoid movement of the "Allow"
+ // button.
+ // Try to keep the content bottom at the same height. If this would move the dialog out of
+ // the top of the screen move it down as much as possible, then keep it at that position for
+ // the rest of the sequence of permission dialogs.
+ View content = getChildAt(0);
+ if (mContentBottom == 0 || content.getMeasuredHeight() > mContentBottom) {
+ mContentBottom = (getMeasuredHeight() + content.getMeasuredHeight()) / 2;
+ }
+ final int contentLeft = (getMeasuredWidth() - content.getMeasuredWidth()) / 2;
+ final int contentTop = mContentBottom - content.getMeasuredHeight();
+ final int contentRight = contentLeft + content.getMeasuredWidth();
+ content.layout(contentLeft, contentTop, contentRight, mContentBottom);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java
new file mode 100644
index 0000000000000..8aa158b282275
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayTouchActivity.java
@@ -0,0 +1,29 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS;
+
+import android.app.Activity;
+import android.os.Bundle;
+
+public class OverlayTouchActivity extends Activity {
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ getWindow().addPrivateFlags(PRIVATE_FLAG_HIDE_NON_SYSTEM_OVERLAY_WINDOWS);
+ super.onCreate(savedInstanceState);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayWarningDialog.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayWarningDialog.java
new file mode 100644
index 0000000000000..61734b470c785
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/OverlayWarningDialog.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.content.ActivityNotFoundException;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.DialogInterface.OnDismissListener;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+
+import com.android.packageinstaller.R;
+
+public class OverlayWarningDialog extends Activity implements OnClickListener, OnDismissListener {
+
+ private static final String TAG = "OverlayWarningDialog";
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ new AlertDialog.Builder(this)
+ .setTitle(R.string.screen_overlay_title)
+ .setMessage(R.string.screen_overlay_message)
+ .setPositiveButton(R.string.screen_overlay_button, this)
+ .setOnDismissListener(this)
+ .show();
+ }
+
+ @Override
+ public void onDismiss(DialogInterface dialog) {
+ finish();
+ }
+
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ finish();
+ try {
+ startActivity(new Intent(Settings.ACTION_MANAGE_OVERLAY_PERMISSION));
+ } catch (ActivityNotFoundException e) {
+ Log.w(TAG, "No manage overlay settings", e);
+ }
+ }
+
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/PreferenceImageView.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/PreferenceImageView.java
new file mode 100644
index 0000000000000..c3f516744ce28
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/PreferenceImageView.java
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.widget.ImageView;
+
+/**
+ * Extension of ImageView that correctly applies maxWidth and maxHeight.
+ */
+public class PreferenceImageView extends ImageView {
+
+ public PreferenceImageView(Context context) {
+ this(context, null);
+ }
+
+ public PreferenceImageView(Context context, AttributeSet attrs) {
+ this(context, attrs, 0);
+ }
+
+ public PreferenceImageView(Context context, AttributeSet attrs, int defStyleAttr) {
+ this(context, attrs, defStyleAttr, 0);
+ }
+
+ public PreferenceImageView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
+ @Override
+ protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+ final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+ if (widthMode == MeasureSpec.AT_MOST || widthMode == MeasureSpec.UNSPECIFIED) {
+ final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
+ final int maxWidth = getMaxWidth();
+ if (maxWidth != Integer.MAX_VALUE
+ && (maxWidth < widthSize || widthMode == MeasureSpec.UNSPECIFIED)) {
+ widthMeasureSpec = MeasureSpec.makeMeasureSpec(maxWidth, MeasureSpec.AT_MOST);
+ }
+ }
+
+ final int heightMode = MeasureSpec.getMode(heightMeasureSpec);
+ if (heightMode == MeasureSpec.AT_MOST || heightMode == MeasureSpec.UNSPECIFIED) {
+ final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
+ final int maxHeight = getMaxHeight();
+ if (maxHeight != Integer.MAX_VALUE
+ && (maxHeight < heightSize || heightMode == MeasureSpec.UNSPECIFIED)) {
+ heightMeasureSpec = MeasureSpec.makeMeasureSpec(maxHeight, MeasureSpec.AT_MOST);
+ }
+ }
+
+ super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java
new file mode 100644
index 0000000000000..e4929c7e947e6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/ReviewPermissionsActivity.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui;
+
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.text.TextUtils;
+
+import androidx.fragment.app.Fragment;
+import androidx.fragment.app.FragmentActivity;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment
+ .OnActionConfirmedListener;
+import com.android.packageinstaller.permission.ui.handheld.ReviewPermissionsFragment;
+import com.android.packageinstaller.permission.ui.wear.ReviewPermissionsWearFragment;
+
+public final class ReviewPermissionsActivity extends FragmentActivity
+ implements OnActionConfirmedListener {
+
+ @Override
+ protected void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ PackageInfo packageInfo = getTargetPackageInfo();
+ if (packageInfo == null) {
+ finish();
+ return;
+ }
+
+ if (DeviceUtils.isWear(this)) {
+ Fragment fragment = ReviewPermissionsWearFragment.newInstance(packageInfo);
+ getSupportFragmentManager().beginTransaction()
+ .replace(android.R.id.content, fragment).commit();
+ } else {
+ setContentView(R.layout.review_permissions);
+ if (getSupportFragmentManager().findFragmentById(R.id.preferences_frame) == null) {
+ getSupportFragmentManager().beginTransaction().add(R.id.preferences_frame,
+ ReviewPermissionsFragment.newInstance(packageInfo)).commit();
+ }
+ }
+ }
+
+ @Override
+ public void onActionConfirmed(String action) {
+ Fragment fragment = getSupportFragmentManager().findFragmentById(R.id.preferences_frame);
+ if (fragment instanceof OnActionConfirmedListener) {
+ ((OnActionConfirmedListener) fragment).onActionConfirmed(action);
+ }
+ }
+
+ private PackageInfo getTargetPackageInfo() {
+ String packageName = getIntent().getStringExtra(Intent.EXTRA_PACKAGE_NAME);
+ if (TextUtils.isEmpty(packageName)) {
+ return null;
+ }
+ try {
+ return getPackageManager().getPackageInfo(packageName,
+ PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ return null;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/AppPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/AppPermissionsFragment.java
new file mode 100644
index 0000000000000..9bc75fc4ea7f7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/AppPermissionsFragment.java
@@ -0,0 +1,204 @@
+/**
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.auto;
+
+
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.os.Bundle;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Toast;
+
+import androidx.car.widget.ListItem;
+import androidx.car.widget.ListItemAdapter;
+import androidx.car.widget.ListItemProvider;
+import androidx.car.widget.PagedListView;
+import androidx.car.widget.TextListItem;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+
+/**
+ * Contains all permissions in a list for a given application.
+ */
+public final class AppPermissionsFragment extends Fragment{
+
+ private static final String LOG_TAG = "ManagePermsFragment";
+ public static final String EXTRA_LAYOUT = "extra_layout";
+
+ private AppPermissions mAppPermissions;
+
+ private String mPackageName;
+
+ protected PagedListView mListView;
+ protected ListItemAdapter mPagedListAdapter;
+
+
+ /**
+ * Creates a new instance.
+ * @param packageName the packageName of the application that we are listing the
+ * permissions here.
+ */
+ public static AppPermissionsFragment newInstance(String packageName) {
+ AppPermissionsFragment fragment = new AppPermissionsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putInt(EXTRA_LAYOUT, R.layout.car_app_permissions);
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public void onActivityCreated(Bundle savedInstanceState) {
+ super.onActivityCreated(savedInstanceState);
+ getView().findViewById(R.id.action_bar_icon_container).setOnClickListener(
+ v -> getActivity().onBackPressed());
+
+ mListView = (PagedListView) getView().findViewById(R.id.list);
+ mPagedListAdapter = new ListItemAdapter(getContext(), getItemProvider());
+ mListView.setAdapter(mPagedListAdapter);
+ }
+
+ protected void notifyDataSetChanged() {
+ mPagedListAdapter.notifyDataSetChanged();
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ if (savedInstanceState != null
+ && savedInstanceState.containsKey(Intent.EXTRA_PACKAGE_NAME)) {
+ mPackageName = savedInstanceState.getString(Intent.EXTRA_PACKAGE_NAME);
+ } else if (getArguments() != null
+ && getArguments().containsKey(Intent.EXTRA_PACKAGE_NAME)) {
+ mPackageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ }
+
+ if (mPackageName == null) {
+ Log.e(LOG_TAG, "package name is missing");
+ return;
+ }
+ Activity activity = getActivity();
+ PackageInfo packageInfo = getPackageInfo(activity, mPackageName);
+ if (packageInfo == null) {
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
+ activity.finish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(activity, packageInfo, true, new Runnable() {
+ @Override
+ public void run() {
+ activity.finish();
+ }
+ });
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+ outState.putString(Intent.EXTRA_PACKAGE_NAME, mPackageName);
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ return inflater.inflate(getArguments().getInt(EXTRA_LAYOUT), container, false);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+ }
+
+ /**
+ * Gets the list of the LineItems to show up in the list
+ */
+ public ListItemProvider getItemProvider() {
+ ArrayList items = new ArrayList<>();
+ Context context = getContext();
+ if (context == null) {
+ return new ListItemProvider.ListProvider(items);
+ }
+
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (!Utils.shouldShowPermission(group)) {
+ continue;
+ }
+ items.add(new PermissionLineItem(group, context));
+ }
+ return new ListItemProvider.ListProvider(items);
+ }
+
+ private static PackageInfo getPackageInfo(Activity activity, String packageName) {
+ try {
+ return activity.getPackageManager().getPackageInfo(
+ packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ if (Log.isLoggable(LOG_TAG, Log.INFO)) {
+ Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
+ }
+ return null;
+ }
+ }
+
+ private class PermissionLineItem extends TextListItem {
+
+ PermissionLineItem(AppPermissionGroup permissionGroup, Context context) {
+ super(context);
+ setTitle(permissionGroup.getLabel().toString());
+ setPrimaryActionIcon(permissionGroup.getIconResId(), /* useLargeIcon= */ false);
+ setSwitch(
+ permissionGroup.areRuntimePermissionsGranted(),
+ /* showDivider= */ false,
+ (button, isChecked) -> {
+ if (isChecked) {
+ permissionGroup.grantRuntimePermissions(/* fixedByTheUser= */ false);
+ return;
+ }
+ boolean grantedByDefault = permissionGroup.hasGrantedByDefaultPermission();
+ if (!grantedByDefault && permissionGroup.doesSupportRuntimePermissions()) {
+ permissionGroup.revokeRuntimePermissions(/* fixedByTheUser= */ false);
+ return;
+ }
+ new AlertDialog.Builder(context)
+ .setMessage(grantedByDefault
+ ? R.string.system_warning
+ : R.string.old_sdk_deny_warning)
+ .setNegativeButton(R.string.cancel, /* listener= */ null)
+ .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
+ (dialog, which) ->
+ permissionGroup.revokeRuntimePermissions(
+ /* fixedByTheUser= */ false))
+ .show();
+ });
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/GrantPermissionsAutoViewHandler.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
new file mode 100644
index 0000000000000..9e40d6643970f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/auto/GrantPermissionsAutoViewHandler.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.auto;
+
+import android.app.Activity;
+import android.view.WindowManager;
+
+import com.android.packageinstaller.permission.ui.handheld.GrantPermissionsViewHandlerImpl;
+
+/**
+ * A {@link com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler} that is
+ * specific for the auto use-case. In this case, the permissions dialog needs to be larger to make
+ * clicking and reading safer in the car. Otherwise, the UI remains the same.
+ *
+ *
The reason this class extends {@link GrantPermissionsViewHandlerImpl} is so that it can
+ * change the window params to allow the dialog's width to be larger.
+ */
+public class GrantPermissionsAutoViewHandler extends GrantPermissionsViewHandlerImpl {
+ public GrantPermissionsAutoViewHandler(Activity activity, String appPackageName) {
+ super(activity, appPackageName);
+ }
+
+ /**
+ * Update the given {@link android.view.WindowManager.LayoutParams} to allow the dialog to take
+ * up the entirety of the width.
+ */
+ @Override
+ public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) {
+ outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ outLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AllAppPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AllAppPermissionsFragment.java
new file mode 100644
index 0000000000000..1d8e5adfcaf0f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AllAppPermissionsFragment.java
@@ -0,0 +1,378 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceCategory;
+import android.preference.PreferenceGroup;
+import android.provider.Settings;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.view.MenuItem;
+import android.widget.Switch;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.Permission;
+import com.android.packageinstaller.permission.utils.ArrayUtils;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.List;
+
+/**
+ * Show and manage individual permissions for an app.
+ *
+ *
Shows the list of individual runtime and non-runtime permissions the app has requested.
+ */
+public final class AllAppPermissionsFragment extends SettingsWithHeader {
+
+ private static final String LOG_TAG = "AllAppPermissionsFragment";
+
+ private static final String KEY_OTHER = "other_perms";
+
+ private static final String EXTRA_FILTER_GROUP =
+ "com.android.packageinstaller.extra.FILTER_GROUP";
+
+ private List mGroups;
+
+ public static AllAppPermissionsFragment newInstance(String packageName) {
+ return newInstance(packageName, null);
+ }
+
+ public static AllAppPermissionsFragment newInstance(String packageName, String filterGroup) {
+ AllAppPermissionsFragment instance = new AllAppPermissionsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ arguments.putString(EXTRA_FILTER_GROUP, filterGroup);
+ instance.setArguments(arguments);
+ return instance;
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ // If we target a group make this look like app permissions.
+ if (getArguments().getString(EXTRA_FILTER_GROUP) == null) {
+ ab.setTitle(R.string.all_permissions);
+ } else {
+ ab.setTitle(R.string.app_permissions);
+ }
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ setHasOptionsMenu(true);
+
+ updateUi();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void updateUi() {
+ if (getPreferenceScreen() != null) {
+ getPreferenceScreen().removeAll();
+ }
+ addPreferencesFromResource(R.xml.all_permissions);
+ PreferenceGroup otherGroup = (PreferenceGroup) findPreference(KEY_OTHER);
+ ArrayList prefs = new ArrayList<>(); // Used for sorting.
+ prefs.add(otherGroup);
+ String pkg = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ String filterGroup = getArguments().getString(EXTRA_FILTER_GROUP);
+ otherGroup.removeAll();
+ PackageManager pm = getContext().getPackageManager();
+
+ try {
+ PackageInfo info = pm.getPackageInfo(pkg, PackageManager.GET_PERMISSIONS);
+
+ ApplicationInfo appInfo = info.applicationInfo;
+ final Drawable icon =
+ IconDrawableFactory.newInstance(getContext()).getBadgedIcon(appInfo);
+ final CharSequence label = appInfo.loadLabel(pm);
+ Intent infoIntent = null;
+ if (!getActivity().getIntent().getBooleanExtra(
+ AppPermissionsFragment.EXTRA_HIDE_INFO_BUTTON, false)) {
+ infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", pkg, null));
+ }
+ setHeader(icon, label, infoIntent);
+
+ if (info.requestedPermissions != null) {
+ for (int i = 0; i < info.requestedPermissions.length; i++) {
+ PermissionInfo perm;
+ try {
+ perm = pm.getPermissionInfo(info.requestedPermissions[i], 0);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG,
+ "Can't get permission info for " + info.requestedPermissions[i], e);
+ continue;
+ }
+
+ if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (perm.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ continue;
+ }
+
+ if (appInfo.isInstantApp()
+ && (perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT)
+ == 0) {
+ continue;
+ }
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && (perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+ != 0) {
+ continue;
+ }
+
+ if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS) {
+ PackageItemInfo group = getGroup(perm.group, pm);
+ if (group == null) {
+ group = perm;
+ }
+ // If we show a targeted group, then ignore everything else.
+ if (filterGroup != null && !group.name.equals(filterGroup)) {
+ continue;
+ }
+ PreferenceGroup pref = findOrCreate(group, pm, prefs);
+ pref.addPreference(getPreference(info, perm, group, pm));
+ } else if (filterGroup == null) {
+ if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_NORMAL) {
+ PermissionGroupInfo group = getGroup(perm.group, pm);
+ otherGroup.addPreference(getPreference(info,
+ perm, group, pm));
+ }
+ }
+
+ // If we show a targeted group, then don't show 'other' permissions.
+ if (filterGroup != null) {
+ getPreferenceScreen().removePreference(otherGroup);
+ }
+ }
+ }
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Problem getting package info for " + pkg, e);
+ }
+ // Sort an ArrayList of the groups and then set the order from the sorting.
+ Collections.sort(prefs, new Comparator() {
+ @Override
+ public int compare(Preference lhs, Preference rhs) {
+ String lKey = lhs.getKey();
+ String rKey = rhs.getKey();
+ if (lKey.equals(KEY_OTHER)) {
+ return 1;
+ } else if (rKey.equals(KEY_OTHER)) {
+ return -1;
+ } else if (Utils.isModernPermissionGroup(lKey)
+ != Utils.isModernPermissionGroup(rKey)) {
+ return Utils.isModernPermissionGroup(lKey) ? -1 : 1;
+ }
+ return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
+ }
+ });
+ for (int i = 0; i < prefs.size(); i++) {
+ prefs.get(i).setOrder(i);
+ }
+ }
+
+ private PermissionGroupInfo getGroup(String group, PackageManager pm) {
+ try {
+ return pm.getPermissionGroupInfo(group, 0);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ private PreferenceGroup findOrCreate(PackageItemInfo group, PackageManager pm,
+ ArrayList prefs) {
+ PreferenceGroup pref = (PreferenceGroup) findPreference(group.name);
+ if (pref == null) {
+ pref = new PreferenceCategory(getContext());
+ pref.setKey(group.name);
+ pref.setTitle(group.loadLabel(pm));
+ prefs.add(pref);
+ getPreferenceScreen().addPreference(pref);
+ }
+ return pref;
+ }
+
+ private Preference getPreference(PackageInfo packageInfo, PermissionInfo perm,
+ PackageItemInfo group, PackageManager pm) {
+ final Preference pref;
+
+ // We allow individual permission control for some permissions if review enabled
+ final boolean mutable = Utils.isPermissionIndividuallyControlled(getContext(), perm.name);
+ if (mutable) {
+ pref = new MyMultiTargetSwitchPreference(getContext(), perm.name,
+ getPermissionForegroundGroup(packageInfo, perm.name));
+ } else {
+ pref = new Preference(getContext());
+ }
+
+ Drawable icon = null;
+ if (perm.icon != 0) {
+ icon = perm.loadIcon(pm);
+ } else if (group != null && group.icon != 0) {
+ icon = group.loadIcon(pm);
+ } else {
+ icon = getContext().getDrawable(R.drawable.ic_perm_device_info);
+ }
+ pref.setIcon(Utils.applyTint(getContext(), icon, android.R.attr.colorControlNormal));
+ pref.setTitle(perm.loadSafeLabel(pm, 20000, PackageItemInfo.SAFE_LABEL_FLAG_TRIM));
+ pref.setSingleLineTitle(false);
+ final CharSequence desc = perm.loadDescription(pm);
+
+ pref.setOnPreferenceClickListener((Preference preference) -> {
+ new AlertDialog.Builder(getContext())
+ .setMessage(desc)
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ return mutable;
+ });
+
+ return pref;
+ }
+
+ /**
+ * Return the (foreground-) {@link AppPermissionGroup group} a permission belongs to.
+ *
+ *
For foreground or non background-foreground permissions this returns the group
+ * {@link AppPermissionGroup} the permission is in. For background permisisons this returns
+ * the group the matching foreground
+ *
+ * @param packageInfo Package information about the app
+ * @param permission The permission that belongs to a group
+ *
+ * @return the group the permissions belongs to
+ */
+ private AppPermissionGroup getPermissionForegroundGroup(PackageInfo packageInfo,
+ String permission) {
+ AppPermissionGroup appPermissionGroup = null;
+ if (mGroups != null) {
+ final int groupCount = mGroups.size();
+ for (int i = 0; i < groupCount; i++) {
+ AppPermissionGroup currentPermissionGroup = mGroups.get(i);
+ if (currentPermissionGroup.hasPermission(permission)) {
+ appPermissionGroup = currentPermissionGroup;
+ break;
+ }
+ if (currentPermissionGroup.getBackgroundPermissions() != null
+ && currentPermissionGroup.getBackgroundPermissions().hasPermission(
+ permission)) {
+ appPermissionGroup = currentPermissionGroup.getBackgroundPermissions();
+ break;
+ }
+ }
+ }
+ if (appPermissionGroup == null) {
+ appPermissionGroup = AppPermissionGroup.create(
+ getContext(), packageInfo, permission);
+ if (mGroups == null) {
+ mGroups = new ArrayList<>();
+ }
+ mGroups.add(appPermissionGroup);
+ }
+ return appPermissionGroup;
+ }
+
+ private static final class MyMultiTargetSwitchPreference extends MultiTargetSwitchPreference {
+ MyMultiTargetSwitchPreference(Context context, String permission,
+ AppPermissionGroup appPermissionGroup) {
+ super(context);
+
+ setChecked(appPermissionGroup.areRuntimePermissionsGranted(
+ new String[] {permission}));
+
+ setSwitchOnClickListener(v -> {
+ Switch switchView = (Switch) v;
+ if (switchView.isChecked()) {
+ appPermissionGroup.grantRuntimePermissions(false,
+ new String[]{permission});
+ // We are granting a permission from a group but since this is an
+ // individual permission control other permissions in the group may
+ // be revoked, hence we need to mark them user fixed to prevent the
+ // app from requesting a non-granted permission and it being granted
+ // because another permission in the group is granted. This applies
+ // only to apps that support runtime permissions.
+ if (appPermissionGroup.doesSupportRuntimePermissions()) {
+ int grantedCount = 0;
+ String[] revokedPermissionsToFix = null;
+ final int permissionCount = appPermissionGroup.getPermissions().size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission current = appPermissionGroup.getPermissions().get(i);
+ if (!current.isGranted()) {
+ if (!current.isUserFixed()) {
+ revokedPermissionsToFix = ArrayUtils.appendString(
+ revokedPermissionsToFix, current.getName());
+ }
+ } else {
+ grantedCount++;
+ }
+ }
+ if (revokedPermissionsToFix != null) {
+ // If some permissions were not granted then they should be fixed.
+ appPermissionGroup.revokeRuntimePermissions(true,
+ revokedPermissionsToFix);
+ } else if (appPermissionGroup.getPermissions().size() == grantedCount) {
+ // If all permissions are granted then they should not be fixed.
+ appPermissionGroup.grantRuntimePermissions(false);
+ }
+ }
+ } else {
+ appPermissionGroup.revokeRuntimePermissions(true,
+ new String[]{permission});
+ // If we just revoked the last permission we need to clear
+ // the user fixed state as now the app should be able to
+ // request them at runtime if supported.
+ if (appPermissionGroup.doesSupportRuntimePermissions()
+ && !appPermissionGroup.areRuntimePermissionsGranted()) {
+ appPermissionGroup.revokeRuntimePermissions(false);
+ }
+ }
+ });
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AppPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AppPermissionsFragment.java
new file mode 100644
index 0000000000000..917f238b81111
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/AppPermissionsFragment.java
@@ -0,0 +1,331 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.IconDrawableFactory;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+import com.android.packageinstaller.permission.utils.Utils;
+import com.android.settingslib.HelpUtils;
+
+/**
+ * Show and manage permission groups for an app.
+ *
+ *
Shows the list of permission groups the app has requested at one permission for.
+ */
+public final class AppPermissionsFragment extends SettingsWithHeader
+ implements PermissionPreference.PermissionPreferenceChangeListener,
+ PermissionPreference.PermissionPreferenceOwnerFragment {
+
+ private static final String LOG_TAG = "ManagePermsFragment";
+
+ static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
+
+ private static final int MENU_ALL_PERMS = 0;
+
+ private ArraySet mToggledGroups;
+ private AppPermissions mAppPermissions;
+ private PreferenceScreen mExtraScreen;
+
+ private boolean mHasConfirmedRevoke;
+
+ public static AppPermissionsFragment newInstance(String packageName) {
+ return setPackageName(new AppPermissionsFragment(), packageName);
+ }
+
+ private static T setPackageName(T fragment, String packageName) {
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setLoading(true /* loading */, false /* animate */);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ Activity activity = getActivity();
+ PackageInfo packageInfo = getPackageInfo(activity, packageName);
+ if (packageInfo == null) {
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
+ activity.finish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(activity, packageInfo, true, new Runnable() {
+ @Override
+ public void run() {
+ getActivity().finish();
+ }
+ });
+ updatePreferences();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+ updatePreferences();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ getActivity().finish();
+ return true;
+ }
+
+ case MENU_ALL_PERMS: {
+ showAllPermissions(null);
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ if (mAppPermissions != null) {
+ bindUi(this, mAppPermissions.getPackageInfo());
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions);
+ HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions,
+ getClass().getName());
+ }
+
+ private void showAllPermissions(String filterGroup) {
+ Fragment frag = AllAppPermissionsFragment.newInstance(
+ getArguments().getString(Intent.EXTRA_PACKAGE_NAME),
+ filterGroup);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("AllPerms")
+ .commit();
+ }
+
+ private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
+ Activity activity = fragment.getActivity();
+ PackageManager pm = activity.getPackageManager();
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+ Intent infoIntent = null;
+ if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
+ infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", packageInfo.packageName, null));
+ }
+
+ Drawable icon = IconDrawableFactory.newInstance(activity).getBadgedIcon(appInfo);
+ CharSequence label = appInfo.loadLabel(pm);
+ fragment.setHeader(icon, label, infoIntent);
+
+ ActionBar ab = activity.getActionBar();
+ if (ab != null) {
+ ab.setTitle(R.string.app_permissions);
+ }
+ }
+
+ private void updatePreferences() {
+ Context context = getActivity();
+ if (context == null) {
+ return;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(screen);
+ }
+
+ screen.removeAll();
+
+ if (mExtraScreen != null) {
+ mExtraScreen.removeAll();
+ }
+
+ final Preference extraPerms = new Preference(context);
+ extraPerms.setIcon(R.drawable.ic_toc);
+ extraPerms.setTitle(R.string.additional_permissions);
+
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (!Utils.shouldShowPermission(group)) {
+ continue;
+ }
+
+ boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
+
+ PermissionPreference preference = new PermissionPreference(this, group, this);
+ preference.setKey(group.getName());
+ Drawable icon = Utils.loadDrawable(context.getPackageManager(),
+ group.getIconPkg(), group.getIconResId());
+ preference.setIcon(Utils.applyTint(getContext(), icon,
+ android.R.attr.colorControlNormal));
+ preference.setTitle(group.getLabel());
+
+ if (isPlatform) {
+ screen.addPreference(preference);
+ } else {
+ if (mExtraScreen == null) {
+ mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
+ }
+ mExtraScreen.addPreference(preference);
+ }
+ }
+
+ if (mExtraScreen != null) {
+ extraPerms.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
+ setPackageName(frag, getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
+ frag.setTargetFragment(AppPermissionsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack(null)
+ .commit();
+ return true;
+ }
+ });
+ int count = mExtraScreen.getPreferenceCount();
+ extraPerms.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, count, count));
+ screen.addPreference(extraPerms);
+ }
+
+ setLoading(false /* loading */, true /* animate */);
+ }
+
+ @Override
+ public void onPreferenceChanged(String key) {
+ if (mToggledGroups == null) {
+ mToggledGroups = new ArraySet<>();
+ }
+ mToggledGroups.add(mAppPermissions.getPermissionGroup(key));
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ logToggledGroups();
+ }
+
+ private void logToggledGroups() {
+ if (mToggledGroups != null) {
+ SafetyNetLogger.logPermissionsToggled(mToggledGroups);
+ mToggledGroups = null;
+ }
+ }
+
+ private static PackageInfo getPackageInfo(Activity activity, String packageName) {
+ try {
+ return activity.getPackageManager().getPackageInfo(
+ packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
+ return null;
+ }
+ }
+
+ @Override
+ public boolean shouldConfirmDefaultPermissionRevoke() {
+ return !mHasConfirmedRevoke;
+ }
+
+ @Override
+ public void hasConfirmDefaultPermissionRevoke() {
+ mHasConfirmedRevoke = true;
+ }
+
+ @Override
+ public void onBackgroundAccessChosen(String key, int chosenItem) {
+ ((PermissionPreference) getPreferenceScreen().findPreference(key))
+ .onBackgroundAccessChosen(chosenItem);
+ }
+
+ @Override
+ public void onDenyAnyWay(String key, @PermissionPreference.ChangeTarget int changeTarget) {
+ ((PermissionPreference) getPreferenceScreen().findPreference(key)).onDenyAnyWay(
+ changeTarget);
+ }
+
+ public static class AdditionalPermissionsFragment extends SettingsWithHeader {
+ AppPermissionsFragment mOuterFragment;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mOuterFragment = (AppPermissionsFragment) getTargetFragment();
+ super.onCreate(savedInstanceState);
+ setHeader(mOuterFragment.mIcon, mOuterFragment.mLabel, mOuterFragment.mInfoIntent);
+ setHasOptionsMenu(true);
+ setPreferenceScreen(mOuterFragment.mExtraScreen);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ bindUi(this, getPackageInfo(getActivity(), packageName));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java
new file mode 100644
index 0000000000000..a9e5fc7f34113
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/GrantPermissionsViewHandlerImpl.java
@@ -0,0 +1,446 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.transition.ChangeBounds;
+import android.transition.Transition;
+import android.transition.TransitionManager;
+import android.transition.TransitionSet;
+import android.transition.TransitionValues;
+import android.transition.Visibility;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.view.WindowManager.LayoutParams;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.view.animation.AnimationUtils;
+import android.widget.Button;
+import android.widget.CheckBox;
+import android.widget.ImageView;
+import android.widget.RadioButton;
+import android.widget.RadioGroup;
+import android.widget.Space;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.ui.ButtonBarLayout;
+import com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler;
+import com.android.packageinstaller.permission.ui.ManagePermissionsActivity;
+import com.android.packageinstaller.permission.ui.ManualLayoutFrame;
+
+public class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler,
+ OnClickListener, RadioGroup.OnCheckedChangeListener {
+
+ public static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
+ public static final String ARG_GROUP_COUNT = "ARG_GROUP_COUNT";
+ public static final String ARG_GROUP_INDEX = "ARG_GROUP_INDEX";
+ public static final String ARG_GROUP_ICON = "ARG_GROUP_ICON";
+ public static final String ARG_GROUP_MESSAGE = "ARG_GROUP_MESSAGE";
+ private static final String ARG_GROUP_DETAIL_MESSAGE = "ARG_GROUP_DETAIL_MESSAGE";
+
+ public static final String ARG_GROUP_SHOW_DO_NOT_ASK = "ARG_GROUP_SHOW_DO_NOT_ASK";
+ private static final String ARG_GROUP_SHOW_FOREGOUND_CHOOSER =
+ "ARG_GROUP_SHOW_FOREGOUND_CHOOSER";
+
+ public static final String ARG_GROUP_DO_NOT_ASK_CHECKED = "ARG_GROUP_DO_NOT_ASK_CHECKED";
+ private static final String ARG_GROUP_ALWAYS_OPTION_CHECKED = "ARG_GROUP_ALWAYS_OPTION_CHECKED";
+
+ // Animation parameters.
+ private static final long SWITCH_TIME_MILLIS = 75;
+ private static final long ANIMATION_DURATION_MILLIS = 200;
+
+ private final Activity mActivity;
+ private final String mAppPackageName;
+ private final boolean mPermissionsIndividuallyControlled;
+
+ private ResultListener mResultListener;
+
+ // Configuration of the current dialog
+ private String mGroupName;
+ private int mGroupCount;
+ private int mGroupIndex;
+ private Icon mGroupIcon;
+ private CharSequence mGroupMessage;
+ private CharSequence mDetailMessage;
+
+ private boolean mShowDonNotAsk;
+ private boolean mShowForegroundChooser;
+
+ // Views
+ private ImageView mIconView;
+ private TextView mCurrentGroupView;
+ private TextView mMessageView;
+ private TextView mDetailMessageView;
+ private CheckBox mDoNotAskCheckbox;
+ private RadioGroup mForegroundChooser;
+ private RadioButton mForegroundOnlyOption;
+ private RadioButton mAlwaysOption;
+ private RadioButton mDenyAndDontAskAgainOption;
+ private Button mAllowButton;
+ private Button mMoreInfoButton;
+ private ManualLayoutFrame mRootView;
+ private ViewGroup mContentContainer;
+ private Space mSpacer;
+
+ public GrantPermissionsViewHandlerImpl(Activity activity, String appPackageName) {
+ mActivity = activity;
+ mAppPackageName = appPackageName;
+ mPermissionsIndividuallyControlled =
+ activity.getPackageManager().arePermissionsIndividuallyControlled();
+ }
+
+ @Override
+ public GrantPermissionsViewHandlerImpl setResultListener(ResultListener listener) {
+ mResultListener = listener;
+ return this;
+ }
+
+ @Override
+ public void saveInstanceState(Bundle arguments) {
+ arguments.putString(ARG_GROUP_NAME, mGroupName);
+ arguments.putInt(ARG_GROUP_COUNT, mGroupCount);
+ arguments.putInt(ARG_GROUP_INDEX, mGroupIndex);
+ arguments.putParcelable(ARG_GROUP_ICON, mGroupIcon);
+ arguments.putCharSequence(ARG_GROUP_MESSAGE, mGroupMessage);
+ arguments.putCharSequence(ARG_GROUP_DETAIL_MESSAGE, mDetailMessage);
+
+ arguments.putBoolean(ARG_GROUP_SHOW_DO_NOT_ASK, mShowDonNotAsk);
+ arguments.putBoolean(ARG_GROUP_SHOW_FOREGOUND_CHOOSER, mShowForegroundChooser);
+
+ arguments.putBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED, isDoNotAskAgainChecked());
+ arguments.putBoolean(ARG_GROUP_ALWAYS_OPTION_CHECKED, mAlwaysOption.isSelected());
+ }
+
+ @Override
+ public void loadInstanceState(Bundle savedInstanceState) {
+ mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
+ mGroupMessage = savedInstanceState.getCharSequence(ARG_GROUP_MESSAGE);
+ mGroupIcon = savedInstanceState.getParcelable(ARG_GROUP_ICON);
+ mGroupCount = savedInstanceState.getInt(ARG_GROUP_COUNT);
+ mGroupIndex = savedInstanceState.getInt(ARG_GROUP_INDEX);
+ mDetailMessage = savedInstanceState.getCharSequence(ARG_GROUP_DETAIL_MESSAGE);
+
+ mShowDonNotAsk = savedInstanceState.getBoolean(ARG_GROUP_SHOW_DO_NOT_ASK);
+ mShowForegroundChooser = savedInstanceState.getBoolean(ARG_GROUP_SHOW_FOREGOUND_CHOOSER);
+
+ updateAll(savedInstanceState.getBoolean(ARG_GROUP_DO_NOT_ASK_CHECKED),
+ savedInstanceState.getBoolean(ARG_GROUP_ALWAYS_OPTION_CHECKED));
+ }
+
+ @Override
+ public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
+ CharSequence message, CharSequence detailMessage, boolean showForegroundChooser,
+ boolean showDonNotAsk) {
+ boolean isNewGroup = mGroupIndex != groupIndex;
+ boolean isDoNotAskAgainChecked = mDoNotAskCheckbox.isChecked();
+ boolean isAlwaysOptionChecked = mAlwaysOption.isChecked();
+ if (isNewGroup) {
+ isDoNotAskAgainChecked = false;
+ isAlwaysOptionChecked = false;
+ }
+
+ mGroupName = groupName;
+ mGroupCount = groupCount;
+ mGroupIndex = groupIndex;
+ mGroupIcon = icon;
+ mGroupMessage = message;
+ mShowDonNotAsk = showDonNotAsk;
+ mDetailMessage = detailMessage;
+ mShowForegroundChooser = showForegroundChooser;
+
+ // If this is a second (or later) permission and the views exist, then animate.
+ if (mIconView != null) {
+ if (isNewGroup) {
+ animateToPermission(isDoNotAskAgainChecked, isAlwaysOptionChecked);
+ } else {
+ updateAll(isDoNotAskAgainChecked, isAlwaysOptionChecked);
+ }
+ }
+ }
+
+ private void updateAll(boolean isDoNotAskAgainChecked, boolean isAlwaysOptionChecked) {
+ updateDescription();
+ updateDetailDescription();
+ updateGroup();
+ updateDoNotAskCheckBoxAndForegroundOption(isDoNotAskAgainChecked, isAlwaysOptionChecked);
+ }
+
+ private void animateToPermission(boolean isDoNotAskAgainChecked,
+ boolean isAlwaysOptionChecked) {
+ final View newContent = bindNewContent();
+
+ updateDescription();
+ updateDetailDescription();
+ updateDoNotAskCheckBoxAndForegroundOption(isDoNotAskAgainChecked, isAlwaysOptionChecked);
+ // Update group when the content changes (in onAppear below)
+
+ final View oldView = mContentContainer.getChildAt(0);
+
+ // Grow or shrink the content container to size of new content
+ ChangeBounds growShrinkToNewContentSize = new ChangeBounds();
+ growShrinkToNewContentSize.setDuration(ANIMATION_DURATION_MILLIS);
+ growShrinkToNewContentSize.setInterpolator(AnimationUtils.loadInterpolator(mActivity,
+ android.R.interpolator.fast_out_slow_in));
+
+ // With a delay hide the old content and show the new content
+ Visibility changeContent = new Visibility() {
+ @Override
+ public Animator onAppear(ViewGroup sceneRoot, View view, TransitionValues startValues,
+ TransitionValues endValues) {
+ view.setVisibility(View.INVISIBLE);
+
+ ValueAnimator v = ValueAnimator.ofFloat(0, 1);
+
+ v.addListener(new AnimatorListenerAdapter() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ view.setVisibility(View.VISIBLE);
+ updateGroup();
+ }
+ });
+
+ return v;
+ }
+
+ @Override
+ public Animator onDisappear(ViewGroup sceneRoot, final View view,
+ TransitionValues startValues, TransitionValues endValues) {
+ ValueAnimator v = ValueAnimator.ofFloat(0, 1);
+
+ int[] location = new int[2];
+ // The removed view is put into the overlay that is relative to the window. Hence
+ // it does not get moved along with the changing parent view. This is done manually
+ // here.
+ v.addUpdateListener(animation -> {
+ mContentContainer.getLocationInWindow(location);
+ view.setTop(location[1]);
+ });
+
+ return v;
+ }
+ };
+ changeContent.setDuration(SWITCH_TIME_MILLIS);
+
+ TransitionSet combinedAnimation = new TransitionSet();
+ combinedAnimation.addTransition(growShrinkToNewContentSize);
+ combinedAnimation.addTransition(changeContent);
+ combinedAnimation.setOrdering(TransitionSet.ORDERING_TOGETHER);
+ combinedAnimation.setMatchOrder(Transition.MATCH_INSTANCE);
+
+ TransitionManager.beginDelayedTransition(mRootView, combinedAnimation);
+ mContentContainer.removeView(oldView);
+ mContentContainer.addView(newContent);
+ }
+
+ /**
+ * Update this objects fields to point to the a content view. A content view encapsulates the
+ * permission request message, the detail message, the always deny checkbox, and the foreground
+ * chooser.
+ *
+ * @return The new content view
+ */
+ private View bindNewContent() {
+ ViewGroup content = (ViewGroup) LayoutInflater.from(mActivity)
+ .inflate(R.layout.grant_permissions_content, mContentContainer, false);
+
+ mMessageView = content.requireViewById(R.id.permission_message);
+ mDetailMessageView = content.requireViewById(R.id.detail_message);
+ mIconView = content.requireViewById(R.id.permission_icon);
+ mDoNotAskCheckbox = content.requireViewById(R.id.do_not_ask_checkbox);
+ mSpacer = content.requireViewById(R.id.detail_message_do_not_ask_checkbox_space);
+ mForegroundChooser = content.requireViewById(R.id.foreground_or_always_radiogroup);
+ mForegroundOnlyOption = content.requireViewById(R.id.foreground_only_radio_button);
+ mAlwaysOption = content.requireViewById(R.id.always_radio_button);
+ mDenyAndDontAskAgainOption = content.requireViewById(R.id.deny_dont_ask_again_radio_button);
+
+ mDoNotAskCheckbox.setOnClickListener(this);
+ mDenyAndDontAskAgainOption.setOnClickListener(this);
+ mForegroundChooser.setOnCheckedChangeListener(this);
+
+ return content;
+ }
+
+ @Override
+ public View createView() {
+ mRootView = (ManualLayoutFrame) LayoutInflater.from(mActivity)
+ .inflate(R.layout.grant_permissions, null);
+ mContentContainer = mRootView.requireViewById(R.id.content_container);
+ mContentContainer.removeAllViews();
+ mContentContainer.addView(bindNewContent());
+
+ mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text);
+ mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);
+ mAllowButton.setOnClickListener(this);
+
+ if (mPermissionsIndividuallyControlled) {
+ mMoreInfoButton = (Button) mRootView.findViewById(R.id.permission_more_info_button);
+ mMoreInfoButton.setVisibility(View.VISIBLE);
+ mMoreInfoButton.setOnClickListener(this);
+ }
+
+ mRootView.findViewById(R.id.permission_deny_button).setOnClickListener(this);
+
+ ((ButtonBarLayout) mRootView.requireViewById(R.id.button_group)).setAllowStacking(true);
+
+ if (mGroupName != null) {
+ updateAll(false, false);
+ }
+
+ return mRootView;
+ }
+
+ @Override
+ public void updateWindowAttributes(LayoutParams outLayoutParams) {
+ // No-op
+ }
+
+ private void updateDescription() {
+ if (mGroupIcon != null) {
+ mIconView.setImageDrawable(mGroupIcon.loadDrawable(mActivity));
+ }
+ mMessageView.setText(mGroupMessage);
+ }
+
+ private void updateDetailDescription() {
+ if (mDetailMessage == null) {
+ mDetailMessageView.setVisibility(View.GONE);
+ mSpacer.setVisibility(View.GONE);
+ } else {
+ if (mShowDonNotAsk) {
+ mSpacer.setVisibility(View.VISIBLE);
+ } else {
+ mSpacer.setVisibility(View.GONE);
+ }
+
+ mDetailMessageView.setText(mDetailMessage);
+ mDetailMessageView.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void updateGroup() {
+ if (mGroupCount > 1) {
+ mCurrentGroupView.setVisibility(View.VISIBLE);
+ mCurrentGroupView.setText(mActivity.getString(R.string.current_permission_template,
+ mGroupIndex + 1, mGroupCount));
+ } else {
+ mCurrentGroupView.setVisibility(View.GONE);
+ }
+ }
+
+ private void updateDoNotAskCheckBoxAndForegroundOption(boolean isDoNotAskAgainChecked,
+ boolean isAlwaysSelected) {
+ if (mShowForegroundChooser) {
+ mForegroundChooser.setVisibility(View.VISIBLE);
+ mDoNotAskCheckbox.setVisibility(View.GONE);
+
+ if (isAlwaysSelected) {
+ mAlwaysOption.setSelected(true);
+ }
+
+ if (mShowDonNotAsk) {
+ mDenyAndDontAskAgainOption.setSelected(isDoNotAskAgainChecked);
+ mDenyAndDontAskAgainOption.setVisibility(View.VISIBLE);
+ } else {
+ mDenyAndDontAskAgainOption.setVisibility(View.GONE);
+ }
+ } else {
+ mForegroundChooser.setVisibility(View.GONE);
+ if (mShowDonNotAsk) {
+ mDoNotAskCheckbox.setVisibility(View.VISIBLE);
+ mDoNotAskCheckbox.setChecked(isDoNotAskAgainChecked);
+ } else {
+ mDoNotAskCheckbox.setVisibility(View.GONE);
+ }
+ }
+
+ mAllowButton.setEnabled(!isDoNotAskAgainChecked() && isOptionChosenIfNeeded());
+ }
+
+ private boolean isDoNotAskAgainChecked() {
+ return (mDoNotAskCheckbox.getVisibility() == View.VISIBLE
+ && mDoNotAskCheckbox.isChecked())
+ || (mDenyAndDontAskAgainOption.getVisibility() == View.VISIBLE
+ && mDenyAndDontAskAgainOption.isChecked());
+ }
+
+ private boolean isOptionChosenIfNeeded() {
+ return !mShowForegroundChooser
+ || (mForegroundOnlyOption.isChecked()
+ || (mDenyAndDontAskAgainOption.getVisibility() == View.VISIBLE
+ && mDenyAndDontAskAgainOption.isChecked())
+ || (mAlwaysOption.getVisibility() == View.VISIBLE && mAlwaysOption.isChecked()));
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.permission_allow_button:
+ if (mResultListener != null) {
+ view.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
+ if (mShowForegroundChooser && mForegroundOnlyOption.isChecked()) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ GRANTED_FOREGROUND_ONLY);
+ } else {
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
+ }
+ }
+ break;
+ case R.id.permission_deny_button:
+ if (mResultListener != null) {
+ view.performAccessibilityAction(
+ AccessibilityNodeInfo.ACTION_CLEAR_ACCESSIBILITY_FOCUS, null);
+ if (isDoNotAskAgainChecked()) {
+ mResultListener.onPermissionGrantResult(mGroupName,
+ DENIED_DO_NOT_ASK_AGAIN);
+ } else {
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED);
+ }
+ }
+ break;
+ case R.id.permission_more_info_button:
+ Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, mAppPackageName);
+ intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
+ mActivity.startActivity(intent);
+ break;
+ }
+
+ mAllowButton.setEnabled(!isDoNotAskAgainChecked() && isOptionChosenIfNeeded());
+
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED);
+ }
+ }
+
+ @Override
+ public void onCheckedChanged(RadioGroup group, int checkedId) {
+ mAllowButton.setEnabled(!isDoNotAskAgainChecked() && isOptionChosenIfNeeded());
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageCustomPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageCustomPermissionsFragment.java
new file mode 100644
index 0000000000000..4f740e2c0cfd6
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageCustomPermissionsFragment.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.handheld;
+
+import android.view.MenuItem;
+
+/**
+ * Fragment that allows the user to manage custom permissions.
+ */
+public class ManageCustomPermissionsFragment extends ManagePermissionsFragment {
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getActivity().setTitle(com.android.packageinstaller.R.string.additional_permissions);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void updatePermissionsUi() {
+ updatePermissionsUi(false);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManagePermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManagePermissionsFragment.java
new file mode 100644
index 0000000000000..e50a1d89ff76e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManagePermissionsFragment.java
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.app.ActionBar;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.util.ArraySet;
+import android.util.Log;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.PermissionApps.PmCache;
+import com.android.packageinstaller.permission.model.PermissionGroup;
+import com.android.packageinstaller.permission.model.PermissionGroups;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.List;
+
+/**
+ * Superclass for fragments allowing the user to manage permissions.
+ */
+abstract class ManagePermissionsFragment extends PermissionsFrameFragment
+ implements PermissionGroups.PermissionsGroupsChangeCallback,
+ Preference.OnPreferenceClickListener {
+ private static final String LOG_TAG = "ManagePermissionsFragment";
+
+ static final String OS_PKG = "android";
+
+ private ArraySet mLauncherPkgs;
+
+ private PermissionGroups mPermissions;
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setLoading(true /* loading */, false /* animate */);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ mLauncherPkgs = Utils.getLauncherPackages(getContext());
+ mPermissions = new PermissionGroups(getContext(), getLoaderManager(), this);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String key = preference.getKey();
+
+ PermissionGroup group = mPermissions.getGroup(key);
+ if (group == null) {
+ return false;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
+ .putExtra(Intent.EXTRA_PERMISSION_NAME, key);
+ try {
+ getActivity().startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOG_TAG, "No app to handle " + intent);
+ }
+
+ return true;
+ }
+
+ /**
+ * @return the permissions
+ */
+ protected PermissionGroups getPermissions() {
+ return mPermissions;
+ }
+
+ @Override
+ public void onPermissionGroupsChanged() {
+ updatePermissionsUi();
+ }
+
+ /**
+ * Update the preferences to show the new {@link #getPermissions() permissions}.
+ */
+ protected abstract void updatePermissionsUi();
+
+ /**
+ * Add preferences for all permissions of a type to the preference screen.
+ *
+ * @param addSystemPermissions If the permissions added should be system permissions or not
+ *
+ * @return The preference screen the permissions were added to
+ */
+ protected PreferenceScreen updatePermissionsUi(boolean addSystemPermissions) {
+ Context context = getActivity();
+ if (context == null) {
+ return null;
+ }
+
+ List groups = mPermissions.getGroups();
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(screen);
+ } else {
+ screen.removeAll();
+ }
+
+ // Use this to speed up getting the info for all of the PermissionApps below.
+ // Create a new one for each refresh to make sure it has fresh data.
+ PmCache cache = new PmCache(getContext().getPackageManager());
+ for (PermissionGroup group : groups) {
+ boolean isSystemPermission = group.getDeclaringPackage().equals(OS_PKG);
+
+ if (addSystemPermissions == isSystemPermission) {
+ Preference preference = findPreference(group.getName());
+
+ if (preference == null) {
+ preference = new Preference(context);
+ preference.setOnPreferenceClickListener(this);
+ preference.setKey(group.getName());
+ preference.setIcon(Utils.applyTint(context, group.getIcon(),
+ android.R.attr.colorControlNormal));
+ preference.setTitle(group.getLabel());
+ // Set blank summary so that no resizing/jumping happens when the summary is
+ // loaded.
+ preference.setSummary(" ");
+ preference.setPersistent(false);
+ screen.addPreference(preference);
+ }
+ preference.setSummary(getString(R.string.app_permissions_group_summary,
+ group.getGranted(), group.getTotal()));
+ }
+ }
+ if (screen.getPreferenceCount() != 0) {
+ setLoading(false /* loading */, true /* animate */);
+ }
+
+ return screen;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageStandardPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageStandardPermissionsFragment.java
new file mode 100644
index 0000000000000..075ab3ce609e9
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ManageStandardPermissionsFragment.java
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package com.android.packageinstaller.permission.ui.handheld;
+
+import android.app.FragmentTransaction;
+import android.preference.Preference;
+import android.preference.PreferenceScreen;
+import android.view.MenuItem;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.PermissionGroup;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.List;
+
+/**
+ * Fragment that allows the user to manage standard permissions.
+ */
+public final class ManageStandardPermissionsFragment extends ManagePermissionsFragment {
+ private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
+
+ /**
+ * @return A new fragment
+ */
+ public static ManageStandardPermissionsFragment newInstance() {
+ return new ManageStandardPermissionsFragment();
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ getActivity().setTitle(com.android.packageinstaller.R.string.app_permissions);
+ }
+
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ getActivity().finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ protected void updatePermissionsUi() {
+ PreferenceScreen screen = updatePermissionsUi(true);
+ if (screen == null) {
+ return;
+ }
+
+ // Check if we need an additional permissions preference
+ List groups = getPermissions().getGroups();
+ int numExtraPermissions = 0;
+ for (PermissionGroup group : groups) {
+ if (!group.getDeclaringPackage().equals(ManagePermissionsFragment.OS_PKG)) {
+ numExtraPermissions++;
+ }
+ }
+
+ Preference additionalPermissionsPreference = screen.findPreference(EXTRA_PREFS_KEY);
+ if (numExtraPermissions == 0) {
+ if (additionalPermissionsPreference != null) {
+ screen.removePreference(additionalPermissionsPreference);
+ }
+ } else {
+ if (additionalPermissionsPreference == null) {
+ additionalPermissionsPreference = new Preference(getActivity());
+ additionalPermissionsPreference.setKey(EXTRA_PREFS_KEY);
+ additionalPermissionsPreference.setIcon(Utils.applyTint(getActivity(),
+ R.drawable.ic_more_items,
+ android.R.attr.colorControlNormal));
+ additionalPermissionsPreference.setTitle(R.string.additional_permissions);
+ additionalPermissionsPreference.setOnPreferenceClickListener(preference -> {
+ ManageCustomPermissionsFragment frag =
+ new ManageCustomPermissionsFragment();
+ frag.setTargetFragment(ManageStandardPermissionsFragment.this, 0);
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.replace(android.R.id.content, frag);
+ ft.addToBackStack(null);
+ ft.commit();
+ return true;
+ });
+
+ screen.addPreference(additionalPermissionsPreference);
+ }
+
+ additionalPermissionsPreference.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, numExtraPermissions,
+ numExtraPermissions));
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/MultiTargetSwitchPreference.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/MultiTargetSwitchPreference.java
new file mode 100644
index 0000000000000..dfd721a29d6c9
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/MultiTargetSwitchPreference.java
@@ -0,0 +1,60 @@
+/*
+* Copyright (C) 2016 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+* See the License for the specific language governing permissions and
+* limitations under the License.
+*/
+
+package com.android.packageinstaller.permission.ui.handheld;
+
+import android.content.Context;
+import android.preference.SwitchPreference;
+import android.view.View;
+import android.widget.Switch;
+
+class MultiTargetSwitchPreference extends SwitchPreference {
+ private View.OnClickListener mSwitchOnClickLister;
+
+ public MultiTargetSwitchPreference(Context context) {
+ super(context);
+ }
+
+ public void setCheckedOverride(boolean checked) {
+ super.setChecked(checked);
+ }
+
+ @Override
+ public void setChecked(boolean checked) {
+ // If double target behavior is enabled do nothing
+ if (mSwitchOnClickLister == null) {
+ super.setChecked(checked);
+ }
+ }
+
+ public void setSwitchOnClickListener(View.OnClickListener listener) {
+ mSwitchOnClickLister = listener;
+ }
+
+ @Override
+ protected void onBindView(View view) {
+ super.onBindView(view);
+ Switch switchView = (Switch) view.findViewById(
+ com.android.internal.R.id.switch_widget);
+ switchView.setOnClickListener(mSwitchOnClickLister);
+
+ if (mSwitchOnClickLister != null) {
+ final int padding = (int) ((view.getMeasuredHeight()
+ - switchView.getMeasuredHeight()) / 2 + 0.5f);
+ switchView.setPadding(padding, padding, 0, padding);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionAppsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionAppsFragment.java
new file mode 100644
index 0000000000000..b2a37ac53214d
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionAppsFragment.java
@@ -0,0 +1,390 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.app.ActionBar;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.preference.Preference;
+import android.preference.Preference.OnPreferenceClickListener;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.util.ArraySet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.PermissionApps;
+import com.android.packageinstaller.permission.model.PermissionApps.Callback;
+import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+import com.android.packageinstaller.permission.utils.Utils;
+import com.android.settingslib.HelpUtils;
+
+/**
+ * Show and manage apps which request a single permission group.
+ *
+ *
Shows a list of apps which request at least on permission of this group.
+ */
+public final class PermissionAppsFragment extends PermissionsFrameFragment implements Callback,
+ PermissionPreference.PermissionPreferenceOwnerFragment,
+ PermissionPreference.PermissionPreferenceChangeListener {
+
+ private static final int MENU_SHOW_SYSTEM = Menu.FIRST;
+ private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1;
+ private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
+
+ private static final String SHOW_SYSTEM_KEY = PermissionAppsFragment.class.getName()
+ + KEY_SHOW_SYSTEM_PREFS;
+
+ public static PermissionAppsFragment newInstance(String permissionName) {
+ return setPermissionName(new PermissionAppsFragment(), permissionName);
+ }
+
+ private static T setPermissionName(T fragment, String permissionName) {
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ private PermissionApps mPermissionApps;
+
+ private PreferenceScreen mExtraScreen;
+
+ private ArraySet mToggledGroups;
+ private ArraySet mLauncherPkgs;
+ private boolean mHasConfirmedRevoke;
+
+ private boolean mShowSystem;
+ private boolean mHasSystemApps;
+ private MenuItem mShowSystemMenu;
+ private MenuItem mHideSystemMenu;
+
+ private Callback mOnPermissionsLoadedListener;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ if (savedInstanceState != null) {
+ mShowSystem = savedInstanceState.getBoolean(SHOW_SYSTEM_KEY);
+ }
+
+ setLoading(true /* loading */, false /* animate */);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ mLauncherPkgs = Utils.getLauncherPackages(getContext());
+
+ String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ mPermissionApps = new PermissionApps(getActivity(), groupName, this);
+ mPermissionApps.refresh(true);
+ }
+
+ @Override
+ public void onSaveInstanceState(Bundle outState) {
+ super.onSaveInstanceState(outState);
+
+ outState.putBoolean(SHOW_SYSTEM_KEY, mShowSystem);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mPermissionApps.refresh(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (mHasSystemApps) {
+ mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
+ R.string.menu_show_system);
+ mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE,
+ R.string.menu_hide_system);
+ updateMenu();
+ }
+
+ HelpUtils.prepareHelpMenuItem(getActivity(), menu, R.string.help_app_permissions,
+ getClass().getName());
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getActivity().finish();
+ return true;
+ case MENU_SHOW_SYSTEM:
+ case MENU_HIDE_SYSTEM:
+ mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM;
+ if (mPermissionApps.getApps() != null) {
+ onPermissionsLoaded(mPermissionApps);
+ }
+ updateMenu();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void updateMenu() {
+ mShowSystemMenu.setVisible(!mShowSystem);
+ mHideSystemMenu.setVisible(mShowSystem);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bindUi(this, mPermissionApps);
+ }
+
+ private static void bindUi(Fragment fragment, PermissionApps permissionApps) {
+ final Drawable icon = permissionApps.getIcon();
+ final CharSequence label = permissionApps.getLabel();
+ final ActionBar ab = fragment.getActivity().getActionBar();
+ if (ab != null) {
+ ab.setTitle(label);
+ }
+ }
+
+ private void setOnPermissionsLoadedListener(Callback callback) {
+ mOnPermissionsLoadedListener = callback;
+ }
+
+ @Override
+ public void onPermissionsLoaded(PermissionApps permissionApps) {
+ Context context = getActivity();
+
+ if (context == null) {
+ return;
+ }
+
+ boolean isTelevision = DeviceUtils.isTelevision(context);
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(screen);
+ }
+
+ screen.setOrderingAsAdded(false);
+
+ ArraySet preferencesToRemove = new ArraySet<>();
+ for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
+ preferencesToRemove.add(screen.getPreference(i).getKey());
+ }
+ if (mExtraScreen != null) {
+ for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
+ preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
+ }
+ }
+
+ mHasSystemApps = false;
+ boolean menuOptionsInvalided = false;
+
+ for (PermissionApp app : permissionApps.getApps()) {
+ if (!Utils.shouldShowPermission(app.getPermissionGroup())) {
+ continue;
+ }
+
+ if (!app.getAppInfo().enabled) {
+ continue;
+ }
+
+ String key = app.getKey();
+ preferencesToRemove.remove(key);
+ Preference existingPref = screen.findPreference(key);
+ if (existingPref == null && mExtraScreen != null) {
+ existingPref = mExtraScreen.findPreference(key);
+ }
+
+ boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
+
+ if (isSystemApp && !menuOptionsInvalided) {
+ mHasSystemApps = true;
+ getActivity().invalidateOptionsMenu();
+ menuOptionsInvalided = true;
+ }
+
+ if (isSystemApp && !isTelevision && !mShowSystem) {
+ if (existingPref != null) {
+ screen.removePreference(existingPref);
+ }
+ continue;
+ }
+
+ if (existingPref != null) {
+ ((PermissionPreference) existingPref).updateUi();
+ continue;
+ }
+
+ PermissionPreference pref = new PermissionPreference(this, app.getPermissionGroup(),
+ this);
+ pref.setKey(app.getKey());
+ pref.setIcon(app.getIcon());
+ pref.setTitle(app.getLabel());
+
+ if (isSystemApp && isTelevision) {
+ if (mExtraScreen == null) {
+ mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
+ }
+ mExtraScreen.addPreference(pref);
+ } else {
+ screen.addPreference(pref);
+ }
+ }
+
+ if (mExtraScreen != null) {
+ preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
+ Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
+
+ if (pref == null) {
+ pref = new Preference(context);
+ pref.setKey(KEY_SHOW_SYSTEM_PREFS);
+ pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
+ android.R.attr.colorControlNormal));
+ pref.setTitle(R.string.preference_show_system_apps);
+ pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ SystemAppsFragment frag = new SystemAppsFragment();
+ setPermissionName(frag, getArguments().getString(Intent.EXTRA_PERMISSION_NAME));
+ frag.setTargetFragment(PermissionAppsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("SystemApps")
+ .commit();
+ return true;
+ }
+ });
+ screen.addPreference(pref);
+ }
+
+ int grantedCount = 0;
+ for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
+ if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
+ grantedCount++;
+ }
+ }
+ pref.setSummary(getString(R.string.app_permissions_group_summary,
+ grantedCount, mExtraScreen.getPreferenceCount()));
+ }
+
+ for (String key : preferencesToRemove) {
+ Preference pref = screen.findPreference(key);
+ if (pref != null) {
+ screen.removePreference(pref);
+ } else if (mExtraScreen != null) {
+ pref = mExtraScreen.findPreference(key);
+ if (pref != null) {
+ mExtraScreen.removePreference(pref);
+ }
+ }
+ }
+
+ setLoading(false /* loading */, true /* animate */);
+
+ if (mOnPermissionsLoadedListener != null) {
+ mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
+ }
+ }
+
+ @Override
+ public void onPreferenceChanged(String key) {
+ if (mToggledGroups == null) {
+ mToggledGroups = new ArraySet<>();
+ }
+ mToggledGroups.add(mPermissionApps.getApp(key).getPermissionGroup());
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ logToggledGroups();
+ }
+
+ private void logToggledGroups() {
+ if (mToggledGroups != null) {
+ SafetyNetLogger.logPermissionsToggled(mToggledGroups);
+ mToggledGroups = null;
+ }
+ }
+
+ @Override
+ public void onBackgroundAccessChosen(String key, int chosenItem) {
+ ((PermissionPreference) getPreferenceScreen().findPreference(key))
+ .onBackgroundAccessChosen(chosenItem);
+ }
+
+ @Override
+ public void onDenyAnyWay(String key, @PermissionPreference.ChangeTarget int changeTarget) {
+ ((PermissionPreference) getPreferenceScreen().findPreference(key)).onDenyAnyWay(
+ changeTarget);
+ }
+
+ @Override
+ public boolean shouldConfirmDefaultPermissionRevoke() {
+ return !mHasConfirmedRevoke;
+ }
+
+ @Override
+ public void hasConfirmDefaultPermissionRevoke() {
+ mHasConfirmedRevoke = true;
+ }
+
+ public static class SystemAppsFragment extends PermissionsFrameFragment implements Callback {
+ PermissionAppsFragment mOuterFragment;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mOuterFragment = (PermissionAppsFragment) getTargetFragment();
+ setLoading(true /* loading */, false /* animate */);
+ super.onCreate(savedInstanceState);
+ if (mOuterFragment.mExtraScreen != null) {
+ setPreferenceScreen();
+ } else {
+ mOuterFragment.setOnPermissionsLoadedListener(this);
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
+ bindUi(this, permissionApps);
+ }
+
+ @Override
+ public void onPermissionsLoaded(PermissionApps permissionApps) {
+ setPreferenceScreen();
+ mOuterFragment.setOnPermissionsLoadedListener(null);
+ }
+
+ private void setPreferenceScreen() {
+ setPreferenceScreen(mOuterFragment.mExtraScreen);
+ setLoading(false /* loading */, true /* animate */);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionPreference.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionPreference.java
new file mode 100644
index 0000000000000..8563705b4f9ad
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionPreference.java
@@ -0,0 +1,581 @@
+/*
+ * Copyright (C) 2018 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.handheld;
+
+import static com.android.packageinstaller.permission.utils.Utils.getRequestMessage;
+
+import static java.lang.annotation.RetentionPolicy.SOURCE;
+
+import android.app.AlertDialog;
+import android.app.Dialog;
+import android.app.DialogFragment;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.pm.PackageItemInfo;
+import android.os.Bundle;
+import android.text.BidiFormatter;
+import android.widget.Switch;
+
+import androidx.annotation.IntDef;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.Permission;
+import com.android.packageinstaller.permission.utils.LocationUtils;
+import com.android.packageinstaller.permission.utils.Utils;
+import com.android.settingslib.RestrictedLockUtils;
+
+import java.lang.annotation.Retention;
+import java.util.List;
+
+/**
+ * A preference for representing a permission group requested by an app.
+ */
+class PermissionPreference extends RestrictedSwitchPreference {
+ @Retention(SOURCE)
+ @IntDef(value = {CHANGE_FOREGROUND, CHANGE_BACKGROUND}, flag = true)
+ @interface ChangeTarget {}
+ static final int CHANGE_FOREGROUND = 1;
+ static final int CHANGE_BACKGROUND = 2;
+ static final int CHANGE_BOTH = CHANGE_FOREGROUND | CHANGE_BACKGROUND;
+
+ private final AppPermissionGroup mGroup;
+ private final Fragment mFragment;
+ private final PermissionPreferenceChangeListener mCallBacks;
+
+ /** Callbacks for the permission to the fragment showing a list of permissions */
+ interface PermissionPreferenceChangeListener {
+ /**
+ * Checks if the user has to confirm a revocation of a permission granted by default.
+ *
+ * @return {@code true} iff the user has to confirm it
+ */
+ boolean shouldConfirmDefaultPermissionRevoke();
+
+ /**
+ * Notify the listener that the user confirmed that she/he wants to revoke permissions that
+ * were granted by default.
+ */
+ void hasConfirmDefaultPermissionRevoke();
+
+ /**
+ * Notify the listener that this preference has changed.
+ *
+ * @param key The key uniquely identifying this preference
+ */
+ void onPreferenceChanged(String key);
+ }
+
+ /**
+ * Callbacks from dialogs to the fragment. These callbacks are supposed to directly cycle back
+ * to the permission tha created the dialog.
+ */
+ interface PermissionPreferenceOwnerFragment {
+ /**
+ * The {@link DefaultDenyDialog} can only interact with the fragment, not the preference
+ * that created it. Hence this call goes to the fragment, which then finds the preference an
+ * calls {@link #onDenyAnyWay(int)}.
+ *
+ * @param key Key uniquely identifying the preference that created the default deny dialog
+ * @param changeTarget Whether background or foreground permissions should be changed
+ *
+ * @see #showDefaultDenyDialog(int)
+ */
+ void onDenyAnyWay(String key, @ChangeTarget int changeTarget);
+
+ /**
+ * The {@link BackgroundAccessChooser} can only interact with the fragment, not the
+ * preference that created it. Hence this call goes to the fragment, which then finds the
+ * preference an calls {@link #onBackgroundAccessChosen(int)}}.
+ *
+ * @param key Key uniquely identifying the preference that created the background access
+ * chooser
+ * @param chosenItem The index of the item selected by the user.
+ *
+ * @see #showBackgroundChooserDialog()
+ */
+ void onBackgroundAccessChosen(String key, int chosenItem);
+ }
+
+ PermissionPreference(Fragment fragment, AppPermissionGroup group,
+ PermissionPreferenceChangeListener callbacks) {
+ super(fragment.getContext());
+
+ mFragment = fragment;
+ mGroup = group;
+ mCallBacks = callbacks;
+
+ setPersistent(false);
+ updateUi();
+ }
+
+ /**
+ * Update the preference after the state might have changed.
+ */
+ void updateUi() {
+ boolean arePermissionsIndividuallyControlled =
+ Utils.areGroupPermissionsIndividuallyControlled(getContext(), mGroup.getName());
+ RestrictedLockUtils.EnforcedAdmin admin =
+ RestrictedLockUtils.getProfileOrDeviceOwner(getContext(), mGroup.getUserId());
+ boolean isForegroundPolicyFixed = mGroup.isPolicyFixed();
+ boolean isBackgroundPolicyFixed = mGroup.getBackgroundPermissions() == null
+ || mGroup.getBackgroundPermissions().isPolicyFixed();
+ boolean isAdminFixed = admin != null;
+ boolean isPolicyFixed = (isForegroundPolicyFixed && isBackgroundPolicyFixed)
+ && !isAdminFixed;
+
+ // Reset ui state
+ setDisabledByAdmin(null);
+ setEnabled(true);
+ setOnPreferenceClickListener(null);
+ setSwitchOnClickListener(null);
+ setSummary(null);
+
+ setChecked(mGroup.areRuntimePermissionsGranted());
+
+ if (isAdminFixed) {
+ setDisabledByAdmin(admin);
+ setSummary(R.string.permission_summary_enforced_by_admin);
+ setEnabled(false);
+ } else if (isPolicyFixed) {
+ // Both foreground and background filed
+ setSummary(R.string.permission_summary_enforced_by_policy);
+ setEnabled(false);
+ } else if (arePermissionsIndividuallyControlled) {
+ setOnPreferenceClickListener((pref) -> {
+ showAllPermissions(mGroup.getName());
+ return false;
+ });
+
+ setSwitchOnClickListener(v -> {
+ Switch switchView = (Switch) v;
+ requestChange(switchView.isChecked(), CHANGE_BOTH);
+
+ // Update UI as the switch widget might be in wrong state
+ updateUi();
+ });
+
+ updateSummaryForIndividuallyControlledPermissionGroup();
+ } else {
+ if (mGroup.hasPermissionWithBackgroundMode()) {
+ if (mGroup.getBackgroundPermissions() == null) {
+ // The group has background permissions but the app did not request any. I.e.
+ // The app can only switch between 'never" and "only in foreground".
+ setOnPreferenceChangeListener((pref, newValue) ->
+ requestChange((Boolean) newValue, CHANGE_FOREGROUND));
+
+ updateSummaryForPermissionGroupWithBackgroundPermission();
+ } else {
+ AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
+
+ if (isBackgroundPolicyFixed) {
+ setOnPreferenceChangeListener((pref, newValue) ->
+ requestChange((Boolean) newValue, CHANGE_FOREGROUND));
+
+ if (backgroundGroup.areRuntimePermissionsGranted()) {
+ setSummary(R.string
+ .permission_summary_enabled_by_policy_background_only);
+ } else {
+ setSummary(R.string
+ .permission_summary_disabled_by_policy_background_only);
+ }
+ } else if (isForegroundPolicyFixed) {
+ if (mGroup.areRuntimePermissionsGranted()) {
+ setOnPreferenceChangeListener((pref, newValue) ->
+ requestChange((Boolean) newValue, CHANGE_BACKGROUND));
+
+ setSummary(R.string
+ .permission_summary_enabled_by_policy_foreground_only);
+ } else {
+ // Background access can only be enabled once foreground access is
+ // enabled. Hence if foreground access can never be enabled, there is
+ // no point allowing background access to be enabled
+ setSummary(R.string.permission_summary_enforced_by_policy);
+ setEnabled(false);
+ }
+ } else {
+ updateSummaryForPermissionGroupWithBackgroundPermission();
+
+ setOnPreferenceClickListener((pref) -> {
+ showBackgroundChooserDialog();
+ return true;
+ });
+
+ setSwitchOnClickListener(v -> {
+ Switch switchView = (Switch) v;
+
+ if (switchView.isChecked()) {
+ showBackgroundChooserDialog();
+ } else {
+ requestChange(false, CHANGE_BOTH);
+ }
+
+ // Update UI as the switch widget might be in wrong state
+ updateUi();
+ });
+ }
+ }
+ } else {
+ setOnPreferenceChangeListener((pref, newValue) ->
+ requestChange((Boolean) newValue, CHANGE_BOTH));
+ }
+ }
+ }
+
+ /**
+ * Update the summary in the case the permission group has individually controlled permissions.
+ */
+ private void updateSummaryForIndividuallyControlledPermissionGroup() {
+ int revokedCount = 0;
+ List permissions = mGroup.getPermissions();
+ final int permissionCount = permissions.size();
+ for (int i = 0; i < permissionCount; i++) {
+ Permission permission = permissions.get(i);
+ if (mGroup.doesSupportRuntimePermissions()
+ ? !permission.isGranted() : (!permission.isAppOpAllowed()
+ || permission.isReviewRequired())) {
+ revokedCount++;
+ }
+ }
+
+ final int resId;
+ if (revokedCount == 0) {
+ resId = R.string.permission_revoked_none;
+ } else if (revokedCount == permissionCount) {
+ resId = R.string.permission_revoked_all;
+ } else {
+ resId = R.string.permission_revoked_count;
+ }
+
+ String summary = getContext().getString(resId, revokedCount);
+ setSummary(summary);
+ }
+
+ /**
+ * Update the summary of a permission group that has background permission.
+ *
+ *
This does not apply to permission groups that are fixed by policy
+ */
+ private void updateSummaryForPermissionGroupWithBackgroundPermission() {
+ AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
+
+ if (mGroup.areRuntimePermissionsGranted()) {
+ if (backgroundGroup == null) {
+ setSummary(R.string.permission_access_only_foreground);
+ } else {
+ if (backgroundGroup.areRuntimePermissionsGranted()) {
+ setSummary(R.string.permission_access_always);
+ } else {
+ setSummary(R.string.permission_access_only_foreground);
+ }
+ }
+ } else {
+ setSummary(R.string.permission_access_never);
+ }
+ }
+
+ /**
+ * Show all individual permissions in this group in a new fragment.
+ */
+ private void showAllPermissions(String filterGroup) {
+ Fragment frag = AllAppPermissionsFragment.newInstance(mGroup.getApp().packageName,
+ filterGroup);
+ mFragment.getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("AllPerms")
+ .commit();
+ }
+
+ /**
+ * Get the label of the app the permission group belongs to. (App permission groups are all
+ * permissions of a group an app has requested.)
+ *
+ * @return The label of the app
+ */
+ private String getAppLabel() {
+ return BidiFormatter.getInstance().unicodeWrap(
+ mGroup.getApp().applicationInfo.loadSafeLabel(getContext().getPackageManager(),
+ PackageItemInfo.DEFAULT_MAX_LABEL_SIZE_PX,
+ PackageItemInfo.SAFE_LABEL_FLAG_TRIM
+ | PackageItemInfo.SAFE_LABEL_FLAG_FIRST_LINE)
+ .toString());
+ }
+
+ /**
+ * Request to grant/revoke permissions group.
+ *
+ *
Does not handle:
+ *
+ *
Individually granted permissions
+ *
Permission groups with background permissions
+ *
+ *
Does handle:
+ *
+ *
Default grant permissions
+ *
+ *
+ * @param requestGrant If this group should be granted
+ * @param changeTarget Which permission group (foreground/background/both) should be changed
+ * @return If the request was processed.
+ */
+ private boolean requestChange(boolean requestGrant, @ChangeTarget int changeTarget) {
+ if (LocationUtils.isLocationGroupAndProvider(mGroup.getName(),
+ mGroup.getApp().packageName)) {
+ LocationUtils.showLocationDialog(getContext(), getAppLabel());
+ return false;
+ }
+ if (requestGrant) {
+ mCallBacks.onPreferenceChanged(getKey());
+
+ if ((changeTarget & CHANGE_FOREGROUND) != 0) {
+ mGroup.grantRuntimePermissions(false);
+ }
+ if ((changeTarget & CHANGE_BACKGROUND) != 0) {
+ if (mGroup.getBackgroundPermissions() != null) {
+ mGroup.getBackgroundPermissions().grantRuntimePermissions(false);
+ }
+ }
+ } else {
+ boolean requestToRevokeGrantedByDefault = false;
+ if ((changeTarget & CHANGE_FOREGROUND) != 0) {
+ requestToRevokeGrantedByDefault = mGroup.hasGrantedByDefaultPermission();
+ }
+ if ((changeTarget & CHANGE_BACKGROUND) != 0) {
+ if (mGroup.getBackgroundPermissions() != null) {
+ requestToRevokeGrantedByDefault |=
+ mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
+ }
+ }
+
+ if ((requestToRevokeGrantedByDefault || !mGroup.doesSupportRuntimePermissions())
+ && mCallBacks.shouldConfirmDefaultPermissionRevoke()) {
+ showDefaultDenyDialog(changeTarget);
+ return false;
+ } else {
+ mCallBacks.onPreferenceChanged(getKey());
+
+ if ((changeTarget & CHANGE_FOREGROUND) != 0) {
+ mGroup.revokeRuntimePermissions(false);
+ }
+ if ((changeTarget & CHANGE_BACKGROUND) != 0) {
+ if (mGroup.getBackgroundPermissions() != null) {
+ mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
+ }
+ }
+ }
+ }
+
+ updateUi();
+
+ return true;
+ }
+
+ /**
+ * Show a dialog that warns the user that she/he is about to revoke permissions that were
+ * granted by default.
+ *
+ *
The order of operation to revoke a permission granted by default is:
+ *
+ *
+ */
+ private void showBackgroundChooserDialog() {
+ Bundle args = new Bundle();
+ args.putCharSequence(BackgroundAccessChooser.TITLE,
+ getRequestMessage(getAppLabel(), mGroup, getContext(), mGroup.getRequest()));
+ args.putString(BackgroundAccessChooser.KEY, getKey());
+
+
+ if (mGroup.areRuntimePermissionsGranted()) {
+ if (mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
+ args.putInt(BackgroundAccessChooser.SELECTION,
+ BackgroundAccessChooser.ALWAYS_OPTION);
+ } else {
+ args.putInt(BackgroundAccessChooser.SELECTION,
+ BackgroundAccessChooser.FOREGROUND_ONLY_OPTION);
+ }
+ } else {
+ args.putInt(BackgroundAccessChooser.SELECTION, BackgroundAccessChooser.NEVER_OPTION);
+ }
+
+ BackgroundAccessChooser chooserDialog = new BackgroundAccessChooser();
+ chooserDialog.setArguments(args);
+ chooserDialog.show(mFragment.getChildFragmentManager().beginTransaction(),
+ "backgroundChooser");
+ }
+
+ /**
+ * Once we user has confirmed that he/she wants to revoke a permission that was granted by
+ * default, actually revoke the permissions.
+ *
+ * @see #showDefaultDenyDialog(int)
+ */
+ void onDenyAnyWay(@ChangeTarget int changeTarget) {
+ mCallBacks.onPreferenceChanged(getKey());
+
+ boolean hasDefaultPermissions = false;
+ if ((changeTarget & CHANGE_FOREGROUND) != 0) {
+ mGroup.revokeRuntimePermissions(false);
+ hasDefaultPermissions = mGroup.hasGrantedByDefaultPermission();
+ }
+ if ((changeTarget & CHANGE_BACKGROUND) != 0) {
+ if (mGroup.getBackgroundPermissions() != null) {
+ mGroup.getBackgroundPermissions().revokeRuntimePermissions(false);
+ hasDefaultPermissions |=
+ mGroup.getBackgroundPermissions().hasGrantedByDefaultPermission();
+ }
+ }
+
+ if (hasDefaultPermissions) {
+ mCallBacks.hasConfirmDefaultPermissionRevoke();
+ }
+ updateUi();
+ }
+
+ /**
+ * Process the return from a {@link BackgroundAccessChooser} dialog.
+ *
+ *
These dialog are started when the user want to grant a permission group that has
+ * background permissions.
+ *
+ * @param choosenItem The item that the user chose
+ */
+ void onBackgroundAccessChosen(int choosenItem) {
+ AppPermissionGroup backgroundGroup = mGroup.getBackgroundPermissions();
+
+ switch (choosenItem) {
+ case BackgroundAccessChooser.ALWAYS_OPTION:
+ requestChange(true, CHANGE_BOTH);
+ break;
+ case BackgroundAccessChooser.FOREGROUND_ONLY_OPTION:
+ if (backgroundGroup.areRuntimePermissionsGranted()) {
+ requestChange(false, CHANGE_BACKGROUND);
+ }
+ requestChange(true, CHANGE_FOREGROUND);
+ break;
+ case BackgroundAccessChooser.NEVER_OPTION:
+ if (mGroup.areRuntimePermissionsGranted()
+ || mGroup.getBackgroundPermissions().areRuntimePermissionsGranted()) {
+ requestChange(false, CHANGE_BOTH);
+ }
+ break;
+ }
+ }
+
+ /**
+ * A dialog warning the user that she/he is about to deny a permission that was granted by
+ * default.
+ *
+ * @see #showDefaultDenyDialog(int)
+ */
+ public static class DefaultDenyDialog extends DialogFragment {
+ private static final String MSG = DefaultDenyDialog.class.getName() + ".arg.msg";
+ private static final String CHANGE_TARGET = DefaultDenyDialog.class.getName()
+ + ".arg.changeTarget";
+ private static final String KEY = DefaultDenyDialog.class.getName() + ".arg.key";
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder b = new AlertDialog.Builder(getContext())
+ .setMessage(getArguments().getInt(MSG))
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
+ (DialogInterface dialog, int which) -> (
+ (PermissionPreferenceOwnerFragment) getParentFragment())
+ .onDenyAnyWay(getArguments().getString(KEY),
+ getArguments().getInt(CHANGE_TARGET)));
+
+ return b.create();
+ }
+ }
+
+ /**
+ * If a permission group has background permission this chooser is used to let the user
+ * choose how the permission group should be granted.
+ *
+ * @see #showBackgroundChooserDialog()
+ */
+ public static class BackgroundAccessChooser extends DialogFragment {
+ private static final String TITLE = BackgroundAccessChooser.class.getName() + ".arg.title";
+ private static final String KEY = BackgroundAccessChooser.class.getName() + ".arg.key";
+ private static final String SELECTION = BackgroundAccessChooser.class.getName()
+ + ".arg.selection";
+
+ // Needs to match the entries in R.array.background_access_chooser_dialog_choices
+ static final int ALWAYS_OPTION = 0;
+ static final int FOREGROUND_ONLY_OPTION = 1;
+ static final int NEVER_OPTION = 2;
+
+ @Override
+ public Dialog onCreateDialog(Bundle savedInstanceState) {
+ AlertDialog.Builder b = new AlertDialog.Builder(getActivity())
+ .setTitle(getArguments().getCharSequence(TITLE))
+ .setSingleChoiceItems(R.array.background_access_chooser_dialog_choices,
+ getArguments().getInt(SELECTION),
+ (dialog, which) -> {
+ dismissAllowingStateLoss();
+ ((PermissionPreferenceOwnerFragment) getParentFragment())
+ .onBackgroundAccessChosen(getArguments().getString(KEY),
+ which);
+ }
+ );
+
+ return b.create();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionsFrameFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionsFrameFragment.java
new file mode 100644
index 0000000000000..e7f63b2387ff4
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/PermissionsFrameFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.os.Bundle;
+import android.preference.PreferenceFragment;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.ListView;
+import android.widget.TextView;
+import com.android.packageinstaller.R;
+
+public abstract class PermissionsFrameFragment extends PreferenceFragment {
+ private ViewGroup mPreferencesContainer;
+
+ private View mLoadingView;
+ private ViewGroup mPrefsView;
+ private boolean mIsLoading;
+
+ /**
+ * Returns the view group that holds the preferences objects. This will
+ * only be set after {@link #onCreateView} has been called.
+ */
+ protected final ViewGroup getPreferencesContainer() {
+ return mPreferencesContainer;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.permissions_frame, container,
+ false);
+ mPrefsView = (ViewGroup) rootView.findViewById(R.id.prefs_container);
+ if (mPrefsView == null) {
+ mPrefsView = rootView;
+ }
+ mLoadingView = rootView.findViewById(R.id.loading_container);
+ mPreferencesContainer = (ViewGroup) super.onCreateView(
+ inflater, mPrefsView, savedInstanceState);
+ setLoading(mIsLoading, false, true /* force */);
+ mPrefsView.addView(mPreferencesContainer);
+ return rootView;
+ }
+
+ protected void setLoading(boolean loading, boolean animate) {
+ setLoading(loading, animate, false);
+ }
+
+ private void setLoading(boolean loading, boolean animate, boolean force) {
+ if (mIsLoading != loading || force) {
+ mIsLoading = loading;
+ if (getView() == null) {
+ // If there is no created view, there is no reason to animate.
+ animate = false;
+ }
+ if (mPrefsView != null) {
+ setViewShown(mPrefsView, !loading, animate);
+ }
+ if (mLoadingView != null) {
+ setViewShown(mLoadingView, loading, animate);
+ }
+ }
+ }
+
+ @Override
+ public ListView getListView() {
+ ListView listView = super.getListView();
+ if (listView.getEmptyView() == null) {
+ TextView emptyView = (TextView) getView().findViewById(R.id.no_permissions);
+ listView.setEmptyView(emptyView);
+ }
+ return listView;
+ }
+
+ private void setViewShown(final View view, boolean shown, boolean animate) {
+ if (animate) {
+ Animation animation = AnimationUtils.loadAnimation(getContext(),
+ shown ? android.R.anim.fade_in : android.R.anim.fade_out);
+ if (shown) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ animation.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ view.startAnimation(animation);
+ } else {
+ view.clearAnimation();
+ view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/RestrictedSwitchPreference.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/RestrictedSwitchPreference.java
new file mode 100644
index 0000000000000..30ccf3bfd5612
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/RestrictedSwitchPreference.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.handheld;
+
+import android.content.Context;
+import android.preference.PreferenceScreen;
+import android.view.View;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+import com.android.settingslib.RestrictedLockUtils;
+
+import static com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+public class RestrictedSwitchPreference extends MultiTargetSwitchPreference {
+ private final Context mContext;
+ private boolean mDisabledByAdmin;
+ private EnforcedAdmin mEnforcedAdmin;
+ private final int mSwitchWidgetResId;
+
+ public RestrictedSwitchPreference(Context context) {
+ super(context);
+ mSwitchWidgetResId = getWidgetLayoutResource();
+ mContext = context;
+ }
+
+ @Override
+ public void onBindView(View view) {
+ super.onBindView(view);
+ if (mDisabledByAdmin) {
+ view.setEnabled(true);
+ }
+ if (mDisabledByAdmin) {
+ final TextView summaryView = (TextView) view.findViewById(android.R.id.summary);
+ if (summaryView != null) {
+ summaryView.setText(
+ isChecked() ? R.string.enabled_by_admin : R.string.disabled_by_admin);
+ summaryView.setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ @Override
+ public void setEnabled(boolean enabled) {
+ if (enabled && mDisabledByAdmin) {
+ setDisabledByAdmin(null);
+ } else {
+ super.setEnabled(enabled);
+ }
+ }
+
+ public void setDisabledByAdmin(EnforcedAdmin admin) {
+ final boolean disabled = (admin != null ? true : false);
+ mEnforcedAdmin = admin;
+ if (mDisabledByAdmin != disabled) {
+ mDisabledByAdmin = disabled;
+ setWidgetLayoutResource(disabled ? R.layout.restricted_icon : mSwitchWidgetResId);
+ setEnabled(!disabled);
+ }
+ }
+
+ @Override
+ public void performClick(PreferenceScreen preferenceScreen) {
+ if (mDisabledByAdmin) {
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(mContext, mEnforcedAdmin);
+ } else {
+ super.performClick(preferenceScreen);
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ReviewPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ReviewPermissionsFragment.java
new file mode 100644
index 0000000000000..83fef0dbc13f3
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/ReviewPermissionsFragment.java
@@ -0,0 +1,404 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.handheld;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.text.Html;
+import android.text.Spanned;
+import android.view.View;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+import androidx.fragment.app.DialogFragment;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.preference.TwoStatePreference;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.model.Permission;
+import com.android.packageinstaller.permission.ui.ConfirmActionDialogFragment;
+import com.android.packageinstaller.permission.ui.ManagePermissionsActivity;
+import com.android.packageinstaller.permission.utils.ArrayUtils;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class ReviewPermissionsFragment extends PreferenceFragmentCompat
+ implements View.OnClickListener, Preference.OnPreferenceChangeListener,
+ ConfirmActionDialogFragment.OnActionConfirmedListener {
+
+ private static final String EXTRA_PACKAGE_INFO =
+ "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
+
+ private AppPermissions mAppPermissions;
+
+ private Button mContinueButton;
+ private Button mCancelButton;
+ private Button mMoreInfoButton;
+
+ private PreferenceCategory mNewPermissionsCategory;
+ private PreferenceCategory mCurrentPermissionsCategory;
+
+ private boolean mHasConfirmedRevoke;
+
+ public static ReviewPermissionsFragment newInstance(PackageInfo packageInfo) {
+ Bundle arguments = new Bundle();
+ arguments.putParcelable(ReviewPermissionsFragment.EXTRA_PACKAGE_INFO, packageInfo);
+ ReviewPermissionsFragment instance = new ReviewPermissionsFragment();
+ instance.setArguments(arguments);
+ instance.setRetainInstance(true);
+ return instance;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
+ if (packageInfo == null) {
+ activity.finish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(activity, packageInfo, false,
+ () -> getActivity().finish());
+
+ if (mAppPermissions.getPermissionGroups().isEmpty()) {
+ activity.finish();
+ return;
+ }
+
+ boolean reviewRequired = false;
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (group.isReviewRequired()) {
+ reviewRequired = true;
+ break;
+ }
+ }
+
+ if (!reviewRequired) {
+ activity.finish();
+ }
+ }
+
+ @Override
+ public void onViewCreated(@NonNull View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bindUi();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+ loadPreferences();
+ }
+
+ @Override
+ public void onClick(View view) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ if (view == mContinueButton) {
+ confirmPermissionsReview();
+ executeCallback(true);
+ } else if (view == mCancelButton) {
+ executeCallback(false);
+ activity.setResult(Activity.RESULT_CANCELED);
+ } else if (view == mMoreInfoButton) {
+ Intent intent = new Intent(Intent.ACTION_MANAGE_APP_PERMISSIONS);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME,
+ mAppPermissions.getPackageInfo().packageName);
+ intent.putExtra(ManagePermissionsActivity.EXTRA_ALL_PERMISSIONS, true);
+ getActivity().startActivity(intent);
+ }
+ activity.finish();
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ if (mHasConfirmedRevoke) {
+ return true;
+ }
+ if (preference instanceof SwitchPreference) {
+ SwitchPreference switchPreference = (SwitchPreference) preference;
+ if (switchPreference.isChecked()) {
+ showWarnRevokeDialog(switchPreference.getKey());
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public void onActionConfirmed(String action) {
+ Preference preference = getPreferenceManager().findPreference(action);
+ if (preference instanceof SwitchPreference) {
+ SwitchPreference switchPreference = (SwitchPreference) preference;
+ switchPreference.setChecked(false);
+ mHasConfirmedRevoke = true;
+ }
+ }
+
+ private void showWarnRevokeDialog(final String groupName) {
+ DialogFragment fragment = ConfirmActionDialogFragment.newInstance(
+ getString(R.string.old_sdk_deny_warning), groupName);
+ fragment.show(getActivity().getSupportFragmentManager(), fragment.getClass().getName());
+ }
+
+ private void grantReviewedPermission(AppPermissionGroup group) {
+ String[] permissionsToGrant = null;
+ final int permissionCount = group.getPermissions().size();
+ for (int j = 0; j < permissionCount; j++) {
+ final Permission permission = group.getPermissions().get(j);
+ if (permission.isReviewRequired()) {
+ permissionsToGrant = ArrayUtils.appendString(
+ permissionsToGrant, permission.getName());
+ }
+ }
+ if (permissionsToGrant != null) {
+ group.grantRuntimePermissions(false, permissionsToGrant);
+ }
+ }
+
+ private void confirmPermissionsReview() {
+ final List preferenceGroups = new ArrayList<>();
+ if (mNewPermissionsCategory != null) {
+ preferenceGroups.add(mNewPermissionsCategory);
+ preferenceGroups.add(mCurrentPermissionsCategory);
+ } else {
+ preferenceGroups.add(getPreferenceScreen());
+ }
+
+ final int preferenceGroupCount = preferenceGroups.size();
+ for (int groupNum = 0; groupNum < preferenceGroupCount; groupNum++) {
+ final PreferenceGroup preferenceGroup = preferenceGroups.get(groupNum);
+
+ final int preferenceCount = preferenceGroup.getPreferenceCount();
+ for (int prefNum = 0; prefNum < preferenceCount; prefNum++) {
+ Preference preference = preferenceGroup.getPreference(prefNum);
+ if (preference instanceof TwoStatePreference) {
+ TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
+ String groupName = preference.getKey();
+ AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
+ if (twoStatePreference.isChecked()) {
+ grantReviewedPermission(group);
+
+ // TODO: Allow the user to only grant foreground permissions
+ if (group.getBackgroundPermissions() != null) {
+ grantReviewedPermission(group.getBackgroundPermissions());
+ }
+ } else {
+ group.revokeRuntimePermissions(false);
+ if (group.getBackgroundPermissions() != null) {
+ group.getBackgroundPermissions().revokeRuntimePermissions(false);
+ }
+ }
+ group.resetReviewRequired();
+ }
+ }
+ }
+ }
+
+ private void bindUi() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ // Set icon
+ Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
+ activity.getPackageManager());
+ ImageView iconView = activity.requireViewById(R.id.app_icon);
+ iconView.setImageDrawable(icon);
+
+ // Set message
+ final int labelTemplateResId = isPackageUpdated()
+ ? R.string.permission_review_title_template_update
+ : R.string.permission_review_title_template_install;
+ Spanned message = Html.fromHtml(getString(labelTemplateResId,
+ mAppPermissions.getAppLabel()), 0);
+
+ // Set the permission message as the title so it can be announced.
+ activity.setTitle(message.toString());
+
+ // Color the app name.
+ TextView permissionsMessageView = activity.requireViewById(
+ R.id.permissions_message);
+ permissionsMessageView.setText(message);
+
+ mContinueButton = getActivity().requireViewById(R.id.continue_button);
+ mContinueButton.setOnClickListener(this);
+
+ mCancelButton = getActivity().requireViewById(R.id.cancel_button);
+ mCancelButton.setOnClickListener(this);
+
+ if (activity.getPackageManager().arePermissionsIndividuallyControlled()) {
+ mMoreInfoButton = getActivity().requireViewById(
+ R.id.permission_more_info_button);
+ mMoreInfoButton.setOnClickListener(this);
+ mMoreInfoButton.setVisibility(View.VISIBLE);
+ }
+ }
+
+ private void loadPreferences() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(screen);
+ } else {
+ screen.removeAll();
+ }
+
+ mCurrentPermissionsCategory = null;
+ PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
+ mNewPermissionsCategory = null;
+
+ final boolean isPackageUpdated = isPackageUpdated();
+
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (!Utils.shouldShowPermission(group)
+ || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
+ continue;
+ }
+
+ final SwitchPreference preference;
+ Preference cachedPreference = oldNewPermissionsCategory != null
+ ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
+ if (cachedPreference instanceof SwitchPreference) {
+ preference = (SwitchPreference) cachedPreference;
+ } else {
+ preference = new SwitchPreference(getActivity());
+
+ preference.setKey(group.getName());
+ Drawable icon = Utils.loadDrawable(activity.getPackageManager(),
+ group.getIconPkg(), group.getIconResId());
+ preference.setIcon(Utils.applyTint(getContext(), icon,
+ android.R.attr.colorControlNormal));
+ preference.setTitle(group.getLabel());
+ preference.setSummary(group.getDescription());
+ preference.setPersistent(false);
+
+ preference.setOnPreferenceChangeListener(this);
+ }
+
+ preference.setChecked(group.areRuntimePermissionsGranted()
+ || group.isReviewRequired());
+
+ // Mutable state
+ if (group.isPolicyFixed()) {
+ preference.setEnabled(false);
+ preference.setSummary(getString(
+ R.string.permission_summary_enforced_by_policy));
+ } else {
+ preference.setEnabled(true);
+ }
+
+ if (group.isReviewRequired()) {
+ if (!isPackageUpdated) {
+ screen.addPreference(preference);
+ } else {
+ if (mNewPermissionsCategory == null) {
+ mNewPermissionsCategory = new PreferenceCategory(activity);
+ mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
+ mNewPermissionsCategory.setOrder(1);
+ screen.addPreference(mNewPermissionsCategory);
+ }
+ mNewPermissionsCategory.addPreference(preference);
+ }
+ } else {
+ if (mCurrentPermissionsCategory == null) {
+ mCurrentPermissionsCategory = new PreferenceCategory(activity);
+ mCurrentPermissionsCategory.setTitle(R.string.current_permissions_category);
+ mCurrentPermissionsCategory.setOrder(2);
+ screen.addPreference(mCurrentPermissionsCategory);
+ }
+ mCurrentPermissionsCategory.addPreference(preference);
+ }
+ }
+ }
+
+ private boolean isPackageUpdated() {
+ List groups = mAppPermissions.getPermissionGroups();
+ final int groupCount = groups.size();
+ for (int i = 0; i < groupCount; i++) {
+ AppPermissionGroup group = groups.get(i);
+ if (!group.isReviewRequired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void executeCallback(boolean success) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ if (success) {
+ IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
+ if (intent != null) {
+ try {
+ int flagMask = 0;
+ int flagValues = 0;
+ if (activity.getIntent().getBooleanExtra(
+ Intent.EXTRA_RESULT_NEEDED, false)) {
+ flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
+ flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
+ }
+ activity.startIntentSenderForResult(intent, -1, null,
+ flagMask, flagValues, 0);
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
+ }
+ return;
+ }
+ }
+ RemoteCallback callback = activity.getIntent().getParcelableExtra(
+ Intent.EXTRA_REMOTE_CALLBACK);
+ if (callback != null) {
+ Bundle result = new Bundle();
+ result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
+ callback.sendResult(result);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/SettingsWithHeader.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/SettingsWithHeader.java
new file mode 100644
index 0000000000000..d57757964a986
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/handheld/SettingsWithHeader.java
@@ -0,0 +1,84 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.handheld;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+
+public abstract class SettingsWithHeader extends PermissionsFrameFragment
+ implements OnClickListener {
+
+ private View mHeader;
+ protected Intent mInfoIntent;
+ protected Drawable mIcon;
+ protected CharSequence mLabel;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup root = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
+
+ if (!DeviceUtils.isTelevision(getContext())) {
+ mHeader = inflater.inflate(R.layout.header, root, false);
+ getPreferencesContainer().addView(mHeader, 0);
+ updateHeader();
+ }
+
+ return root;
+ }
+
+ public void setHeader(Drawable icon, CharSequence label, Intent infoIntent) {
+ mIcon = icon;
+ mLabel = label;
+ mInfoIntent = infoIntent;
+ updateHeader();
+ }
+
+ private void updateHeader() {
+ if (mHeader != null) {
+ final ImageView appIcon = (ImageView) mHeader.findViewById(R.id.icon);
+ appIcon.setImageDrawable(mIcon);
+
+ final TextView appName = (TextView) mHeader.findViewById(R.id.name);
+ appName.setText(mLabel);
+
+ final View info = mHeader.findViewById(R.id.info);
+ if (mInfoIntent == null) {
+ info.setVisibility(View.GONE);
+ } else {
+ info.setVisibility(View.VISIBLE);
+ info.setClickable(true);
+ info.setOnClickListener(this);
+ }
+ }
+ }
+
+ @Override
+ public void onClick(View v) {
+ getActivity().startActivity(mInfoIntent);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java
new file mode 100644
index 0000000000000..e56a1f7db537a
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AllAppPermissionsFragment.java
@@ -0,0 +1,319 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.Manifest;
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageItemInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageManager.NameNotFoundException;
+import android.content.pm.PermissionGroupInfo;
+import android.content.pm.PermissionInfo;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.Log;
+import android.view.MenuItem;
+
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.SwitchPreference;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+public final class AllAppPermissionsFragment extends SettingsWithHeader {
+
+ private static final String LOG_TAG = "AllAppPermissionsFragment";
+
+ private static final String KEY_OTHER = "other_perms";
+
+ private PackageInfo mPackageInfo;
+
+ private AppPermissions mAppPermissions;
+
+ public static AllAppPermissionsFragment newInstance(String packageName) {
+ AllAppPermissionsFragment instance = new AllAppPermissionsFragment();
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ instance.setArguments(arguments);
+ return instance;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setTitle(R.string.all_permissions);
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ String pkg = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ try {
+ mPackageInfo = getActivity().getPackageManager().getPackageInfo(pkg,
+ PackageManager.GET_PERMISSIONS);
+ } catch (NameNotFoundException e) {
+ getActivity().finish();
+ }
+
+ mAppPermissions = new AppPermissions(getActivity(), mPackageInfo, false,
+ new Runnable() {
+ @Override
+ public void run() {
+ getActivity().finish();
+ }
+ });
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateUi();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private PreferenceGroup getOtherGroup() {
+ PreferenceGroup otherGroup = (PreferenceGroup) findPreference(KEY_OTHER);
+ if (otherGroup == null) {
+ otherGroup = new PreferenceCategory(getPreferenceManager().getContext());
+ otherGroup.setKey(KEY_OTHER);
+ otherGroup.setTitle(getString(R.string.other_permissions));
+ getPreferenceScreen().addPreference(otherGroup);
+ }
+ return otherGroup;
+ }
+
+ private void updateUi() {
+ getPreferenceScreen().removeAll();
+
+ ArrayList prefs = new ArrayList<>(); // Used for sorting.
+ PackageManager pm = getActivity().getPackageManager();
+
+ ApplicationInfo appInfo = mPackageInfo.applicationInfo;
+ final Drawable icon = appInfo.loadIcon(pm);
+ final CharSequence label = appInfo.loadLabel(pm);
+ Intent infoIntent = null;
+ if (!getActivity().getIntent().getBooleanExtra(
+ AppPermissionsFragment.EXTRA_HIDE_INFO_BUTTON, false)) {
+ infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", mPackageInfo.packageName, null));
+ }
+ setHeader(icon, label, infoIntent, null);
+
+ if (mPackageInfo.requestedPermissions != null) {
+ for (int i = 0; i < mPackageInfo.requestedPermissions.length; i++) {
+ PermissionInfo perm;
+ try {
+ perm = pm.getPermissionInfo(mPackageInfo.requestedPermissions[i], 0);
+ } catch (NameNotFoundException e) {
+ Log.e(LOG_TAG, "Can't get permission info for "
+ + mPackageInfo.requestedPermissions[i], e);
+ continue;
+ }
+
+ if ((perm.flags & PermissionInfo.FLAG_INSTALLED) == 0
+ || (perm.flags & PermissionInfo.FLAG_REMOVED) != 0) {
+ continue;
+ }
+ if (appInfo.isInstantApp()
+ && (perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_INSTANT) == 0) {
+ continue;
+ }
+ if (appInfo.targetSdkVersion < Build.VERSION_CODES.M
+ && (perm.protectionLevel & PermissionInfo.PROTECTION_FLAG_RUNTIME_ONLY)
+ != 0) {
+ continue;
+ }
+
+
+ PermissionGroupInfo group = getGroup(perm.group, pm);
+ if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_DANGEROUS) {
+ PreferenceGroup pref = findOrCreate(group != null ? group : perm, pm, prefs);
+ pref.addPreference(getPreference(perm, group));
+ } else if ((perm.protectionLevel & PermissionInfo.PROTECTION_MASK_BASE)
+ == PermissionInfo.PROTECTION_NORMAL) {
+ PreferenceGroup otherGroup = getOtherGroup();
+ if (prefs.indexOf(otherGroup) < 0) {
+ prefs.add(otherGroup);
+ }
+ getOtherGroup().addPreference(getPreference(perm, group));
+ }
+ }
+ }
+
+ // Sort an ArrayList of the groups and then set the order from the sorting.
+ Collections.sort(prefs, new Comparator() {
+ @Override
+ public int compare(Preference lhs, Preference rhs) {
+ String lKey = lhs.getKey();
+ String rKey = rhs.getKey();
+ if (lKey.equals(KEY_OTHER)) {
+ return 1;
+ } else if (rKey.equals(KEY_OTHER)) {
+ return -1;
+ } else if (Utils.isModernPermissionGroup(lKey)
+ != Utils.isModernPermissionGroup(rKey)) {
+ return Utils.isModernPermissionGroup(lKey) ? -1 : 1;
+ }
+ return lhs.getTitle().toString().compareTo(rhs.getTitle().toString());
+ }
+ });
+ for (int i = 0; i < prefs.size(); i++) {
+ prefs.get(i).setOrder(i);
+ }
+ }
+
+ private PermissionGroupInfo getGroup(String group, PackageManager pm) {
+ try {
+ return pm.getPermissionGroupInfo(group, 0);
+ } catch (NameNotFoundException e) {
+ return null;
+ }
+ }
+
+ private PreferenceGroup findOrCreate(PackageItemInfo group, PackageManager pm,
+ ArrayList prefs) {
+ PreferenceGroup pref = (PreferenceGroup) findPreference(group.name);
+ if (pref == null) {
+ pref = new PreferenceCategory(getActivity());
+ pref.setKey(group.name);
+ pref.setLayoutResource(R.layout.preference_category_material);
+ pref.setTitle(group.loadLabel(pm));
+ prefs.add(pref);
+ getPreferenceScreen().addPreference(pref);
+ }
+ return pref;
+ }
+
+ private Preference getPreference(final PermissionInfo perm, final PermissionGroupInfo group) {
+ if (isMutableGranularPermission(perm.name)) {
+ return getMutablePreference(perm, group);
+ } else {
+ return getImmutablePreference(perm, group);
+ }
+ }
+
+ private Preference getMutablePreference(final PermissionInfo perm, PermissionGroupInfo group) {
+ final AppPermissionGroup permGroup = mAppPermissions.getPermissionGroup(group.name);
+ final String[] filterPermissions = new String[]{perm.name};
+
+ // TODO: No hardcoded layouts
+ SwitchPreference pref = new SwitchPreference(getPreferenceManager().getContext());
+ pref.setLayoutResource(R.layout.preference_permissions);
+ pref.setChecked(permGroup.areRuntimePermissionsGranted(filterPermissions));
+ pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group));
+ pref.setTitle(perm.loadLabel(getActivity().getPackageManager()));
+ pref.setPersistent(false);
+
+ pref.setOnPreferenceChangeListener(new OnPreferenceChangeListener() {
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object value) {
+ if (value == Boolean.TRUE) {
+ permGroup.grantRuntimePermissions(false, filterPermissions);
+ } else {
+ permGroup.revokeRuntimePermissions(false, filterPermissions);
+ }
+ return true;
+ }
+ });
+
+ return pref;
+ }
+
+ private Preference getImmutablePreference(final PermissionInfo perm,
+ PermissionGroupInfo group) {
+ final PackageManager pm = getActivity().getPackageManager();
+
+ // TODO: No hardcoded layouts
+ Preference pref = new Preference(getActivity());
+ pref.setLayoutResource(R.layout.preference_permissions);
+ pref.setIcon(getTintedPermissionIcon(getActivity(), perm, group));
+ pref.setTitle(perm.loadLabel(pm));
+ pref.setPersistent(false);
+
+ pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ new AlertDialog.Builder(getActivity())
+ .setMessage(perm.loadDescription(pm))
+ .setPositiveButton(android.R.string.ok, null)
+ .show();
+ return true;
+ }
+ });
+
+ return pref;
+ }
+
+ private static Drawable getTintedPermissionIcon(Context context, PermissionInfo perm,
+ PermissionGroupInfo group) {
+ final Drawable icon;
+ if (perm.icon != 0) {
+ icon = perm.loadIcon(context.getPackageManager());
+ } else if (group != null && group.icon != 0) {
+ icon = group.loadIcon(context.getPackageManager());
+ } else {
+ icon = context.getDrawable(R.drawable.ic_perm_device_info);
+ }
+ return Utils.applyTint(context, icon, android.R.attr.colorControlNormal);
+ }
+
+ private boolean isMutableGranularPermission(String name) {
+ if (!getContext().getPackageManager().arePermissionsIndividuallyControlled()) {
+ return false;
+ }
+ switch (name) {
+ case Manifest.permission.READ_CONTACTS:
+ case Manifest.permission.WRITE_CONTACTS:
+ case Manifest.permission.READ_SMS:
+ case Manifest.permission.READ_CALL_LOG:
+ case Manifest.permission.CALL_PHONE: {
+ return true;
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java
new file mode 100644
index 0000000000000..442ee92876b1b
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/AppPermissionsFragment.java
@@ -0,0 +1,411 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.app.ActionBar;
+import android.app.Activity;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.provider.Settings;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.Toast;
+
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.PreferenceViewHolder;
+import androidx.preference.SwitchPreference;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity;
+import com.android.packageinstaller.permission.utils.LocationUtils;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+import com.android.packageinstaller.permission.utils.Utils;
+
+public final class AppPermissionsFragment extends SettingsWithHeader
+ implements OnPreferenceChangeListener {
+
+ private static final String LOG_TAG = "ManagePermsFragment";
+
+ static final String EXTRA_HIDE_INFO_BUTTON = "hideInfoButton";
+
+ private static final int MENU_ALL_PERMS = 0;
+
+ private ArraySet mToggledGroups;
+ private AppPermissions mAppPermissions;
+ private PreferenceScreen mExtraScreen;
+
+ private boolean mHasConfirmedRevoke;
+
+ public static AppPermissionsFragment newInstance(String packageName) {
+ return setPackageName(new AppPermissionsFragment(), packageName);
+ }
+
+ private static T setPackageName(T fragment, String packageName) {
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setLoading(true /* loading */, false /* animate */);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ Activity activity = getActivity();
+ PackageInfo packageInfo = getPackageInfo(activity, packageName);
+ if (packageInfo == null) {
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
+ activity.finish();
+ return;
+ }
+
+
+ mAppPermissions = new AppPermissions(activity, packageInfo, true,
+ () -> getActivity().finish());
+
+ if (mAppPermissions.isReviewRequired()) {
+ Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, packageName);
+ startActivity(intent);
+ getActivity().finish();
+ return;
+ }
+
+ loadPreferences();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+ loadPreferences();
+ setPreferencesCheckedState();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home: {
+ getActivity().finish();
+ return true;
+ }
+
+ case MENU_ALL_PERMS: {
+ Fragment frag = AllAppPermissionsFragment.newInstance(
+ getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("AllPerms")
+ .commit();
+ return true;
+ }
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ if (mAppPermissions != null) {
+ bindUi(this, mAppPermissions.getPackageInfo());
+ }
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ super.onCreateOptionsMenu(menu, inflater);
+ menu.add(Menu.NONE, MENU_ALL_PERMS, Menu.NONE, R.string.all_permissions);
+ }
+
+ private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
+ Activity activity = fragment.getActivity();
+ PackageManager pm = activity.getPackageManager();
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+ Intent infoIntent = null;
+ if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
+ infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", packageInfo.packageName, null));
+ }
+
+ Drawable icon = appInfo.loadIcon(pm);
+ CharSequence label = appInfo.loadLabel(pm);
+ fragment.setHeader(icon, label, infoIntent, fragment.getString(
+ R.string.app_permissions_decor_title));
+ }
+
+ private void loadPreferences() {
+ Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ screen.removeAll();
+ screen.addPreference(createHeaderLineTwoPreference(context));
+
+ if (mExtraScreen != null) {
+ mExtraScreen.removeAll();
+ mExtraScreen = null;
+ }
+
+ final Preference extraPerms = new Preference(context);
+ extraPerms.setIcon(R.drawable.ic_toc);
+ extraPerms.setTitle(R.string.additional_permissions);
+
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (!Utils.shouldShowPermission(group)) {
+ continue;
+ }
+
+ boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
+
+ SwitchPreference preference = new SwitchPreference(context);
+ preference.setOnPreferenceChangeListener(this);
+ preference.setKey(group.getName());
+ Drawable icon = Utils.loadDrawable(context.getPackageManager(),
+ group.getIconPkg(), group.getIconResId());
+ preference.setIcon(Utils.applyTint(getContext(), icon,
+ android.R.attr.colorControlNormal));
+ preference.setTitle(group.getLabel());
+ if (group.isPolicyFixed()) {
+ preference.setSummary(getString(R.string.permission_summary_enforced_by_policy));
+ }
+ preference.setPersistent(false);
+ preference.setEnabled(!group.isPolicyFixed());
+ preference.setChecked(group.areRuntimePermissionsGranted());
+
+ if (isPlatform) {
+ screen.addPreference(preference);
+ } else {
+ if (mExtraScreen == null) {
+ mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
+ mExtraScreen.addPreference(createHeaderLineTwoPreference(context));
+ }
+ mExtraScreen.addPreference(preference);
+ }
+ }
+
+ if (mExtraScreen != null) {
+ extraPerms.setOnPreferenceClickListener(preference -> {
+ AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
+ setPackageName(frag, getArguments().getString(Intent.EXTRA_PACKAGE_NAME));
+ frag.setTargetFragment(AppPermissionsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack(null)
+ .commit();
+ return true;
+ });
+ int count = mExtraScreen.getPreferenceCount() - 1;
+ extraPerms.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, count, count));
+ screen.addPreference(extraPerms);
+ }
+
+ setLoading(false /* loading */, true /* animate */);
+ }
+
+ /**
+ * Creates a heading below decor_title and above the rest of the preferences. This heading
+ * displays the app name and banner icon. It's used in both system and additional permissions
+ * fragments for each app. The styling used is the same as a leanback preference with a
+ * customized background color
+ * @param context The context the preferences created on
+ * @return The preference header to be inserted as the first preference in the list.
+ */
+ private Preference createHeaderLineTwoPreference(Context context) {
+ Preference headerLineTwo = new Preference(context) {
+ @Override
+ public void onBindViewHolder(PreferenceViewHolder holder) {
+ super.onBindViewHolder(holder);
+ holder.itemView.setBackgroundColor(
+ getResources().getColor(R.color.lb_header_banner_color));
+ }
+ };
+ headerLineTwo.setKey(HEADER_PREFERENCE_KEY);
+ headerLineTwo.setSelectable(false);
+ headerLineTwo.setTitle(mLabel);
+ headerLineTwo.setIcon(mIcon);
+ return headerLineTwo;
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, Object newValue) {
+ String groupName = preference.getKey();
+ final AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
+
+ if (group == null) {
+ return false;
+ }
+
+ addToggledGroup(group);
+
+ if (LocationUtils.isLocationGroupAndProvider(group.getName(), group.getApp().packageName)) {
+ LocationUtils.showLocationDialog(getContext(), mAppPermissions.getAppLabel());
+ return false;
+ }
+ if (newValue == Boolean.TRUE) {
+ group.grantRuntimePermissions(false);
+ } else {
+ final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
+ if (grantedByDefault || (!group.doesSupportRuntimePermissions()
+ && !mHasConfirmedRevoke)) {
+ new AlertDialog.Builder(getContext())
+ .setMessage(grantedByDefault ? R.string.system_warning
+ : R.string.old_sdk_deny_warning)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
+ (dialog, which) -> {
+ ((SwitchPreference) preference).setChecked(false);
+ group.revokeRuntimePermissions(false);
+ if (!grantedByDefault) {
+ mHasConfirmedRevoke = true;
+ }
+ })
+ .show();
+ return false;
+ } else {
+ group.revokeRuntimePermissions(false);
+ }
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ logToggledGroups();
+ }
+
+ private void addToggledGroup(AppPermissionGroup group) {
+ if (mToggledGroups == null) {
+ mToggledGroups = new ArraySet<>();
+ }
+ mToggledGroups.add(group);
+ }
+
+ private void logToggledGroups() {
+ if (mToggledGroups != null) {
+ SafetyNetLogger.logPermissionsToggled(mToggledGroups);
+ mToggledGroups = null;
+ }
+ }
+
+ private void setPreferencesCheckedState() {
+ setPreferencesCheckedState(getPreferenceScreen());
+ if (mExtraScreen != null) {
+ setPreferencesCheckedState(mExtraScreen);
+ }
+ }
+
+ private void setPreferencesCheckedState(PreferenceScreen screen) {
+ int preferenceCount = screen.getPreferenceCount();
+ for (int i = 0; i < preferenceCount; i++) {
+ Preference preference = screen.getPreference(i);
+ if (preference instanceof SwitchPreference) {
+ SwitchPreference switchPref = (SwitchPreference) preference;
+ AppPermissionGroup group = mAppPermissions.getPermissionGroup(switchPref.getKey());
+ if (group != null) {
+ switchPref.setChecked(group.areRuntimePermissionsGranted());
+ }
+ }
+ }
+ }
+
+ private static PackageInfo getPackageInfo(Activity activity, String packageName) {
+ try {
+ return activity.getPackageManager().getPackageInfo(
+ packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
+ return null;
+ }
+ }
+
+ public static class AdditionalPermissionsFragment extends SettingsWithHeader {
+ AppPermissionsFragment mOuterFragment;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mOuterFragment = (AppPermissionsFragment) getTargetFragment();
+ super.onCreate(savedInstanceState);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferenceScreen(mOuterFragment.mExtraScreen);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ bindUi(this, getPackageInfo(getActivity(), packageName));
+ }
+
+ private static void bindUi(SettingsWithHeader fragment, PackageInfo packageInfo) {
+ Activity activity = fragment.getActivity();
+ PackageManager pm = activity.getPackageManager();
+ ApplicationInfo appInfo = packageInfo.applicationInfo;
+ Intent infoIntent = null;
+ if (!activity.getIntent().getBooleanExtra(EXTRA_HIDE_INFO_BUTTON, false)) {
+ infoIntent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
+ .setData(Uri.fromParts("package", packageInfo.packageName, null));
+ }
+
+ Drawable icon = appInfo.loadIcon(pm);
+ CharSequence label = appInfo.loadLabel(pm);
+ fragment.setHeader(icon, label, infoIntent, fragment.getString(
+ R.string.additional_permissions_decor_title));
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/GrantPermissionsViewHandlerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
new file mode 100644
index 0000000000000..c71835734f5e8
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/GrantPermissionsViewHandlerImpl.java
@@ -0,0 +1,137 @@
+package com.android.packageinstaller.permission.ui.television;
+
+import android.content.Context;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Icon;
+import android.os.Bundle;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.WindowManager;
+import android.widget.Button;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.ui.GrantPermissionsViewHandler;
+
+/**
+ * TV-specific view handler for the grant permissions activity.
+ */
+public final class GrantPermissionsViewHandlerImpl implements GrantPermissionsViewHandler, OnClickListener {
+
+ private static final String ARG_GROUP_NAME = "ARG_GROUP_NAME";
+
+ private final Context mContext;
+
+ private ResultListener mResultListener;
+
+ private String mGroupName;
+
+ private LinearLayout mRootView;
+ private TextView mMessageView;
+ private ImageView mIconView;
+ private TextView mCurrentGroupView;
+ private Button mAllowButton;
+ private Button mSoftDenyButton;
+ private Button mHardDenyButton;
+
+ public GrantPermissionsViewHandlerImpl(Context context, String appPackageName) {
+ mContext = context;
+ }
+
+ @Override
+ public GrantPermissionsViewHandlerImpl setResultListener(ResultListener listener) {
+ mResultListener = listener;
+ return this;
+ }
+
+ @Override
+ public View createView() {
+ mRootView = (LinearLayout) LayoutInflater.from(mContext)
+ .inflate(R.layout.grant_permissions, null);
+
+ mMessageView = (TextView) mRootView.findViewById(R.id.permission_message);
+ mIconView = (ImageView) mRootView.findViewById(R.id.permission_icon);
+ mCurrentGroupView = (TextView) mRootView.findViewById(R.id.current_page_text);
+ mAllowButton = (Button) mRootView.findViewById(R.id.permission_allow_button);
+ mSoftDenyButton = (Button) mRootView.findViewById(R.id.permission_deny_button);
+ mHardDenyButton = (Button) mRootView.findViewById(
+ R.id.permission_deny_dont_ask_again_button);
+
+ mAllowButton.setOnClickListener(this);
+ mSoftDenyButton.setOnClickListener(this);
+ mHardDenyButton.setOnClickListener(this);
+
+ return mRootView;
+ }
+
+ @Override
+ public void updateWindowAttributes(WindowManager.LayoutParams outLayoutParams) {
+ outLayoutParams.width = WindowManager.LayoutParams.MATCH_PARENT;
+ outLayoutParams.height = WindowManager.LayoutParams.WRAP_CONTENT;
+ outLayoutParams.format = PixelFormat.OPAQUE;
+ outLayoutParams.gravity = Gravity.BOTTOM;
+ outLayoutParams.type = WindowManager.LayoutParams.TYPE_SYSTEM_DIALOG;
+ outLayoutParams.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+ }
+
+ @Override
+ public void updateUi(String groupName, int groupCount, int groupIndex, Icon icon,
+ CharSequence message, CharSequence detailMessage, boolean showForegroundChooser,
+ boolean showDoNotAsk) {
+ // TODO: Handle detailMessage
+ // TODO: Handle showForegroundChooser
+
+ mGroupName = groupName;
+
+ mMessageView.setText(message);
+
+ if (icon != null) {
+ mIconView.setImageIcon(icon);
+ }
+
+ mHardDenyButton.setVisibility(showDoNotAsk ? View.VISIBLE : View.GONE);
+ if (groupCount > 1) {
+ mCurrentGroupView.setVisibility(View.VISIBLE);
+ mCurrentGroupView.setText(mContext.getString(R.string.current_permission_template,
+ groupIndex + 1, groupCount));
+ } else {
+ mCurrentGroupView.setVisibility(View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public void saveInstanceState(Bundle outState) {
+ outState.putString(ARG_GROUP_NAME, mGroupName);
+ }
+
+ @Override
+ public void loadInstanceState(Bundle savedInstanceState) {
+ mGroupName = savedInstanceState.getString(ARG_GROUP_NAME);
+ }
+
+ @Override
+ public void onClick(View view) {
+ switch (view.getId()) {
+ case R.id.permission_allow_button:
+ mResultListener.onPermissionGrantResult(mGroupName, GRANTED_ALWAYS);
+ break;
+ case R.id.permission_deny_button:
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED);
+ break;
+ case R.id.permission_deny_dont_ask_again_button:
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED_DO_NOT_ASK_AGAIN);
+ break;
+ }
+ }
+
+ @Override
+ public void onBackPressed() {
+ if (mResultListener != null) {
+ mResultListener.onPermissionGrantResult(mGroupName, DENIED);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/ManagePermissionsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/ManagePermissionsFragment.java
new file mode 100644
index 0000000000000..19f01d0f91c69
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/ManagePermissionsFragment.java
@@ -0,0 +1,238 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.app.ActionBar;
+import android.app.FragmentTransaction;
+import android.content.ActivityNotFoundException;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceScreen;
+import android.util.ArraySet;
+import android.util.Log;
+import android.view.MenuItem;
+import android.view.View;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.PermissionApps.PmCache;
+import com.android.packageinstaller.permission.model.PermissionGroup;
+import com.android.packageinstaller.permission.model.PermissionGroups;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.List;
+
+public final class ManagePermissionsFragment extends SettingsWithHeader
+ implements PermissionGroups.PermissionsGroupsChangeCallback, OnPreferenceClickListener {
+ private static final String LOG_TAG = "ManagePermissionsFragment";
+
+ private static final String OS_PKG = "android";
+
+ private static final String EXTRA_PREFS_KEY = "extra_prefs_key";
+
+ private ArraySet mLauncherPkgs;
+
+ private PermissionGroups mPermissions;
+
+ private PreferenceScreen mExtraScreen;
+
+ public static ManagePermissionsFragment newInstance() {
+ return new ManagePermissionsFragment();
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+ setLoading(true /* loading */, false /* animate */);
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+ mLauncherPkgs = Utils.getLauncherPackages(getContext());
+ mPermissions = new PermissionGroups(getContext(), getLoaderManager(), this);
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ if (item.getItemId() == android.R.id.home) {
+ getActivity().finish();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ String key = preference.getKey();
+
+ PermissionGroup group = mPermissions.getGroup(key);
+ if (group == null) {
+ return false;
+ }
+
+ Intent intent = new Intent(Intent.ACTION_MANAGE_PERMISSION_APPS)
+ .putExtra(Intent.EXTRA_PERMISSION_NAME, key);
+ try {
+ getActivity().startActivity(intent);
+ } catch (ActivityNotFoundException e) {
+ Log.w(LOG_TAG, "No app to handle " + intent);
+ }
+
+ return true;
+ }
+
+ @Override
+ public void onPermissionGroupsChanged() {
+ updatePermissionsUi();
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bindPermissionUi(this, getView());
+ }
+
+ private static void bindPermissionUi(SettingsWithHeader fragment, @Nullable View rootView) {
+ if (fragment == null || rootView == null) {
+ return;
+ }
+ fragment.setHeader(null, null, null, fragment.getString(
+ R.string.manage_permissions_decor_title));
+ }
+
+ private void updatePermissionsUi() {
+ Context context = getPreferenceManager().getContext();
+ if (context == null) {
+ return;
+ }
+
+ List groups = mPermissions.getGroups();
+ PreferenceScreen screen = getPreferenceScreen();
+
+ // Use this to speed up getting the info for all of the PermissionApps below.
+ // Create a new one for each refresh to make sure it has fresh data.
+ PmCache cache = new PmCache(getContext().getPackageManager());
+ for (PermissionGroup group : groups) {
+ boolean isSystemPermission = group.getDeclaringPackage().equals(OS_PKG);
+
+ Preference preference = findPreference(group.getName());
+ if (preference == null && mExtraScreen != null) {
+ preference = mExtraScreen.findPreference(group.getName());
+ }
+ if (preference == null) {
+ preference = new Preference(context);
+ preference.setOnPreferenceClickListener(this);
+ preference.setKey(group.getName());
+ preference.setIcon(Utils.applyTint(context, group.getIcon(),
+ android.R.attr.colorControlNormal));
+ preference.setTitle(group.getLabel());
+ // Set blank summary so that no resizing/jumping happens when the summary is loaded.
+ preference.setSummary(" ");
+ preference.setPersistent(false);
+ if (isSystemPermission) {
+ screen.addPreference(preference);
+ } else {
+ if (mExtraScreen == null) {
+ mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
+ }
+ mExtraScreen.addPreference(preference);
+ }
+ }
+
+ preference.setSummary(getString(R.string.app_permissions_group_summary,
+ group.getGranted(), group.getTotal()));
+ }
+
+ if (mExtraScreen != null && mExtraScreen.getPreferenceCount() > 0
+ && screen.findPreference(EXTRA_PREFS_KEY) == null) {
+ Preference extraScreenPreference = new Preference(context);
+ extraScreenPreference.setKey(EXTRA_PREFS_KEY);
+ extraScreenPreference.setIcon(Utils.applyTint(context,
+ R.drawable.ic_more_items,
+ android.R.attr.colorControlNormal));
+ extraScreenPreference.setTitle(R.string.additional_permissions);
+ extraScreenPreference.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ AdditionalPermissionsFragment frag = new AdditionalPermissionsFragment();
+ frag.setTargetFragment(ManagePermissionsFragment.this, 0);
+ FragmentTransaction ft = getFragmentManager().beginTransaction();
+ ft.replace(android.R.id.content, frag);
+ ft.addToBackStack(null);
+ ft.commit();
+ return true;
+ }
+ });
+ int count = mExtraScreen.getPreferenceCount();
+ extraScreenPreference.setSummary(getResources().getQuantityString(
+ R.plurals.additional_permissions_more, count, count));
+ screen.addPreference(extraScreenPreference);
+ }
+ if (screen.getPreferenceCount() != 0) {
+ setLoading(false /* loading */, true /* animate */);
+ }
+ }
+
+ public static class AdditionalPermissionsFragment extends SettingsWithHeader {
+ @Override
+ public void onCreate(Bundle icicle) {
+ setLoading(true /* loading */, false /* animate */);
+ super.onCreate(icicle);
+ getActivity().setTitle(R.string.additional_permissions);
+ setHasOptionsMenu(true);
+ }
+
+ @Override
+ public void onDestroy() {
+ getActivity().setTitle(R.string.app_permissions);
+ super.onDestroy();
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getFragmentManager().popBackStack();
+ return true;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bindPermissionUi(this, getView());
+ }
+
+ private static void bindPermissionUi(SettingsWithHeader fragment, @Nullable View rootView) {
+ if (fragment == null || rootView == null) {
+ return;
+ }
+ fragment.setHeader(null, null, null,
+ fragment.getString(R.string.additional_permissions_decor_title));
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ setPreferenceScreen(((ManagePermissionsFragment) getTargetFragment()).mExtraScreen);
+ setLoading(false /* loading */, true /* animate */);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java
new file mode 100644
index 0000000000000..6a0a9e7c72752
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionAppsFragment.java
@@ -0,0 +1,438 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.app.ActionBar;
+import android.app.AlertDialog;
+import android.app.Fragment;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.util.ArraySet;
+import android.view.Menu;
+import android.view.MenuInflater;
+import android.view.MenuItem;
+import android.view.View;
+import android.widget.TextView;
+
+import androidx.preference.Preference;
+import androidx.preference.Preference.OnPreferenceChangeListener;
+import androidx.preference.Preference.OnPreferenceClickListener;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.PermissionApps;
+import com.android.packageinstaller.permission.model.PermissionApps.Callback;
+import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
+import com.android.packageinstaller.permission.ui.ReviewPermissionsActivity;
+import com.android.packageinstaller.permission.utils.LocationUtils;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+import com.android.packageinstaller.permission.utils.Utils;
+
+public final class PermissionAppsFragment extends SettingsWithHeader implements Callback,
+ OnPreferenceChangeListener {
+
+ private static final int MENU_SHOW_SYSTEM = Menu.FIRST;
+ private static final int MENU_HIDE_SYSTEM = Menu.FIRST + 1;
+ private static final String KEY_SHOW_SYSTEM_PREFS = "_showSystem";
+
+ public static PermissionAppsFragment newInstance(String permissionName) {
+ return setPermissionName(new PermissionAppsFragment(), permissionName);
+ }
+
+ private static T setPermissionName(T fragment, String permissionName) {
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PERMISSION_NAME, permissionName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ private PermissionApps mPermissionApps;
+
+ private PreferenceScreen mExtraScreen;
+
+ private ArraySet mToggledGroups;
+ private ArraySet mLauncherPkgs;
+ private boolean mHasConfirmedRevoke;
+
+ private boolean mShowSystem;
+ private boolean mHasSystemApps;
+ private MenuItem mShowSystemMenu;
+ private MenuItem mHideSystemMenu;
+
+ private Callback mOnPermissionsLoadedListener;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+ setLoading(true /* loading */, false /* animate */);
+ mLauncherPkgs = Utils.getLauncherPackages(getContext());
+ String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ mPermissionApps = new PermissionApps(getActivity(), groupName, this);
+ }
+
+ @Override
+ public void onStart() {
+ super.onStart();
+
+ setHasOptionsMenu(true);
+ final ActionBar ab = getActivity().getActionBar();
+ if (ab != null) {
+ ab.setDisplayHomeAsUpEnabled(true);
+ }
+
+ mPermissionApps.refresh(true);
+ }
+
+ @Override
+ public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
+ if (mHasSystemApps) {
+ mShowSystemMenu = menu.add(Menu.NONE, MENU_SHOW_SYSTEM, Menu.NONE,
+ R.string.menu_show_system);
+ mHideSystemMenu = menu.add(Menu.NONE, MENU_HIDE_SYSTEM, Menu.NONE,
+ R.string.menu_hide_system);
+ updateMenu();
+ }
+ }
+
+ @Override
+ public boolean onOptionsItemSelected(MenuItem item) {
+ switch (item.getItemId()) {
+ case android.R.id.home:
+ getActivity().finish();
+ return true;
+ case MENU_SHOW_SYSTEM:
+ case MENU_HIDE_SYSTEM:
+ mShowSystem = item.getItemId() == MENU_SHOW_SYSTEM;
+ if (mPermissionApps.getApps() != null) {
+ onPermissionsLoaded(mPermissionApps);
+ }
+ updateMenu();
+ break;
+ }
+ return super.onOptionsItemSelected(item);
+ }
+
+ private void updateMenu() {
+ mShowSystemMenu.setVisible(!mShowSystem);
+ mHideSystemMenu.setVisible(mShowSystem);
+ }
+
+ @Override
+ protected void onSetEmptyText(TextView textView) {
+ textView.setText(R.string.no_apps);
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ bindUi(this, mPermissionApps);
+ }
+
+ private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
+ final Drawable icon = permissionApps.getIcon();
+ final CharSequence label = permissionApps.getLabel();
+
+ fragment.setHeader(null, null, null,
+ fragment.getString(R.string.permission_apps_decor_title, label));
+ }
+
+ private void setOnPermissionsLoadedListener(Callback callback) {
+ mOnPermissionsLoadedListener = callback;
+ }
+
+ @Override
+ public void onPermissionsLoaded(PermissionApps permissionApps) {
+ Context context = getPreferenceManager().getContext();
+
+ if (context == null) {
+ return;
+ }
+
+ boolean isTelevision = DeviceUtils.isTelevision(context);
+ PreferenceScreen screen = getPreferenceScreen();
+
+ ArraySet preferencesToRemove = new ArraySet<>();
+ for (int i = 0, n = screen.getPreferenceCount(); i < n; i++) {
+ preferencesToRemove.add(screen.getPreference(i).getKey());
+ }
+ if (mExtraScreen != null) {
+ for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
+ preferencesToRemove.add(mExtraScreen.getPreference(i).getKey());
+ }
+ }
+
+ mHasSystemApps = false;
+ boolean menuOptionsInvalided = false;
+
+ for (PermissionApp app : permissionApps.getApps()) {
+ if (!Utils.shouldShowPermission(app.getPermissionGroup())) {
+ continue;
+ }
+
+ String key = app.getKey();
+ preferencesToRemove.remove(key);
+ Preference existingPref = screen.findPreference(key);
+ if (existingPref == null && mExtraScreen != null) {
+ existingPref = mExtraScreen.findPreference(key);
+ }
+
+ boolean isSystemApp = Utils.isSystem(app, mLauncherPkgs);
+
+ if (isSystemApp && !menuOptionsInvalided) {
+ mHasSystemApps = true;
+ getActivity().invalidateOptionsMenu();
+ menuOptionsInvalided = true;
+ }
+
+ if (isSystemApp && !isTelevision && !mShowSystem) {
+ if (existingPref != null) {
+ screen.removePreference(existingPref);
+ }
+ continue;
+ }
+
+ if (existingPref != null) {
+ // If existing preference - only update its state.
+ if (app.isPolicyFixed()) {
+ existingPref.setSummary(getString(
+ R.string.permission_summary_enforced_by_policy));
+ }
+ existingPref.setPersistent(false);
+ existingPref.setEnabled(!app.isPolicyFixed());
+ if (existingPref instanceof SwitchPreference) {
+ ((SwitchPreference) existingPref)
+ .setChecked(app.areRuntimePermissionsGranted());
+ }
+ continue;
+ }
+
+ SwitchPreference pref = new SwitchPreference(context);
+ pref.setOnPreferenceChangeListener(this);
+ pref.setKey(app.getKey());
+ pref.setIcon(app.getIcon());
+ pref.setTitle(app.getLabel());
+ if (app.isPolicyFixed()) {
+ pref.setSummary(getString(R.string.permission_summary_enforced_by_policy));
+ }
+ pref.setPersistent(false);
+ pref.setEnabled(!app.isPolicyFixed());
+ pref.setChecked(app.areRuntimePermissionsGranted());
+
+ if (isSystemApp && isTelevision) {
+ if (mExtraScreen == null) {
+ mExtraScreen = getPreferenceManager().createPreferenceScreen(context);
+ }
+ mExtraScreen.addPreference(pref);
+ } else {
+ screen.addPreference(pref);
+ }
+ }
+
+ if (mExtraScreen != null) {
+ preferencesToRemove.remove(KEY_SHOW_SYSTEM_PREFS);
+ Preference pref = screen.findPreference(KEY_SHOW_SYSTEM_PREFS);
+
+ if (pref == null) {
+ pref = new Preference(context);
+ pref.setKey(KEY_SHOW_SYSTEM_PREFS);
+ pref.setIcon(Utils.applyTint(context, R.drawable.ic_toc,
+ android.R.attr.colorControlNormal));
+ pref.setTitle(R.string.preference_show_system_apps);
+ pref.setOnPreferenceClickListener(new OnPreferenceClickListener() {
+ @Override
+ public boolean onPreferenceClick(Preference preference) {
+ SystemAppsFragment frag = new SystemAppsFragment();
+ setPermissionName(frag, getArguments().getString(
+ Intent.EXTRA_PERMISSION_NAME));
+ frag.setTargetFragment(PermissionAppsFragment.this, 0);
+ getFragmentManager().beginTransaction()
+ .replace(android.R.id.content, frag)
+ .addToBackStack("SystemApps")
+ .commit();
+ return true;
+ }
+ });
+ screen.addPreference(pref);
+ }
+
+ int grantedCount = 0;
+ for (int i = 0, n = mExtraScreen.getPreferenceCount(); i < n; i++) {
+ if (((SwitchPreference) mExtraScreen.getPreference(i)).isChecked()) {
+ grantedCount++;
+ }
+ }
+ pref.setSummary(getString(R.string.app_permissions_group_summary,
+ grantedCount, mExtraScreen.getPreferenceCount()));
+ }
+
+ for (String key : preferencesToRemove) {
+ Preference pref = screen.findPreference(key);
+ if (pref != null) {
+ screen.removePreference(pref);
+ } else if (mExtraScreen != null) {
+ pref = mExtraScreen.findPreference(key);
+ if (pref != null) {
+ mExtraScreen.removePreference(pref);
+ }
+ }
+ }
+
+ setLoading(false /* loading */, true /* animate */);
+
+ if (mOnPermissionsLoadedListener != null) {
+ mOnPermissionsLoadedListener.onPermissionsLoaded(permissionApps);
+ }
+ }
+
+ @Override
+ public boolean onPreferenceChange(final Preference preference, Object newValue) {
+ String pkg = preference.getKey();
+ final PermissionApp app = mPermissionApps.getApp(pkg);
+
+ if (app == null) {
+ return false;
+ }
+
+ if (LocationUtils.isLocationGroupAndProvider(mPermissionApps.getGroupName(),
+ app.getPackageName())) {
+ LocationUtils.showLocationDialog(getContext(), app.getLabel());
+ return false;
+ }
+
+ addToggledGroup(app.getPackageName(), app.getPermissionGroup());
+
+ if (app.isReviewRequired()) {
+ Intent intent = new Intent(getActivity(), ReviewPermissionsActivity.class);
+ intent.putExtra(Intent.EXTRA_PACKAGE_NAME, app.getPackageName());
+ startActivity(intent);
+ return false;
+ }
+
+ if (newValue == Boolean.TRUE) {
+ app.grantRuntimePermissions();
+ } else {
+ final boolean grantedByDefault = app.hasGrantedByDefaultPermissions();
+ if (grantedByDefault || (!app.doesSupportRuntimePermissions()
+ && !mHasConfirmedRevoke)) {
+ new AlertDialog.Builder(getContext())
+ .setMessage(grantedByDefault ? R.string.system_warning
+ : R.string.old_sdk_deny_warning)
+ .setNegativeButton(R.string.cancel, null)
+ .setPositiveButton(R.string.grant_dialog_button_deny_anyway,
+ new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ ((SwitchPreference) preference).setChecked(false);
+ app.revokeRuntimePermissions();
+ if (!grantedByDefault) {
+ mHasConfirmedRevoke = true;
+ }
+ }
+ })
+ .show();
+ return false;
+ } else {
+ app.revokeRuntimePermissions();
+ }
+ }
+ return true;
+ }
+
+ @Override
+ public void onStop() {
+ super.onStop();
+ logToggledGroups();
+ }
+
+ private void addToggledGroup(String packageName, AppPermissionGroup group) {
+ if (mToggledGroups == null) {
+ mToggledGroups = new ArraySet<>();
+ }
+
+ mToggledGroups.add(group);
+ }
+
+ private void logToggledGroups() {
+ if (mToggledGroups != null) {
+ SafetyNetLogger.logPermissionsToggled(mToggledGroups);
+ mToggledGroups = null;
+ }
+ }
+
+ public static class SystemAppsFragment extends SettingsWithHeader implements Callback {
+ PermissionAppsFragment mOuterFragment;
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ mOuterFragment = (PermissionAppsFragment) getTargetFragment();
+ setLoading(true /* loading */, false /* animate */);
+ super.onCreate(savedInstanceState);
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ if (mOuterFragment.mExtraScreen != null) {
+ setPreferenceScreen();
+ } else {
+ mOuterFragment.setOnPermissionsLoadedListener(this);
+ }
+ }
+
+ @Override
+ public void onViewCreated(View view, Bundle savedInstanceState) {
+ super.onViewCreated(view, savedInstanceState);
+ String groupName = getArguments().getString(Intent.EXTRA_PERMISSION_NAME);
+ PermissionApps permissionApps = new PermissionApps(getActivity(), groupName, null);
+ bindUi(this, permissionApps);
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mOuterFragment.mPermissionApps.refresh(true);
+ }
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ mOuterFragment.setOnPermissionsLoadedListener(null);
+ }
+
+
+ private static void bindUi(SettingsWithHeader fragment, PermissionApps permissionApps) {
+ final CharSequence label = permissionApps.getLabel();
+ fragment.setHeader(null, null, null,
+ fragment.getString(R.string.system_apps_decor_title, label));
+ }
+
+ @Override
+ public void onPermissionsLoaded(PermissionApps permissionApps) {
+ setPreferenceScreen();
+ }
+
+ private void setPreferenceScreen() {
+ setPreferenceScreen(mOuterFragment.mExtraScreen);
+ setLoading(false /* loading */, true /* animate */);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionsFrameFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionsFrameFragment.java
new file mode 100644
index 0000000000000..af4593ae0f649
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/PermissionsFrameFragment.java
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.os.Bundle;
+import androidx.annotation.Nullable;
+import androidx.preference.PreferenceFragment;
+import androidx.leanback.widget.VerticalGridView;
+import androidx.preference.PreferenceScreen;
+import androidx.recyclerview.widget.RecyclerView;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.animation.Animation;
+import android.view.animation.Animation.AnimationListener;
+import android.view.animation.AnimationUtils;
+import android.widget.TextView;
+
+import com.android.packageinstaller.R;
+
+public abstract class PermissionsFrameFragment extends PreferenceFragment {
+
+ // Key identifying the preference used on TV as the extra header in a permission fragment.
+ // This is to distinguish it from the rest of the preferences
+ protected static final String HEADER_PREFERENCE_KEY = "HeaderPreferenceKey";
+
+ private ViewGroup mPreferencesContainer;
+
+ // TV-specific instance variable
+ @Nullable private RecyclerView mGridView;
+
+ private View mLoadingView;
+ private ViewGroup mPrefsView;
+ private boolean mIsLoading;
+
+ /**
+ * Returns the view group that holds the preferences objects. This will
+ * only be set after {@link #onCreateView} has been called.
+ */
+ protected final ViewGroup getPreferencesContainer() {
+ return mPreferencesContainer;
+ }
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup rootView = (ViewGroup) inflater.inflate(R.layout.permissions_frame, container,
+ false);
+ mPrefsView = (ViewGroup) rootView.findViewById(R.id.prefs_container);
+ if (mPrefsView == null) {
+ mPrefsView = rootView;
+ }
+ mLoadingView = rootView.findViewById(R.id.loading_container);
+ mPreferencesContainer = (ViewGroup) super.onCreateView(
+ inflater, mPrefsView, savedInstanceState);
+ setLoading(mIsLoading, false, true /* force */);
+ mPrefsView.addView(mPreferencesContainer);
+ return rootView;
+ }
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ PreferenceScreen preferences = getPreferenceScreen();
+ if (preferences == null) {
+ preferences = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(preferences);
+ }
+ }
+
+ protected void setLoading(boolean loading, boolean animate) {
+ setLoading(loading, animate, false);
+ }
+
+ private void setLoading(boolean loading, boolean animate, boolean force) {
+ if (mIsLoading != loading || force) {
+ mIsLoading = loading;
+ if (getView() == null) {
+ // If there is no created view, there is no reason to animate.
+ animate = false;
+ }
+ if (mPrefsView != null) {
+ setViewShown(mPrefsView, !loading, animate);
+ }
+ if (mLoadingView != null) {
+ setViewShown(mLoadingView, loading, animate);
+ }
+ }
+ }
+
+ private void setViewShown(final View view, boolean shown, boolean animate) {
+ if (animate) {
+ Animation animation = AnimationUtils.loadAnimation(getContext(),
+ shown ? android.R.anim.fade_in : android.R.anim.fade_out);
+ if (shown) {
+ view.setVisibility(View.VISIBLE);
+ } else {
+ animation.setAnimationListener(new AnimationListener() {
+ @Override
+ public void onAnimationStart(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationRepeat(Animation animation) {
+ }
+
+ @Override
+ public void onAnimationEnd(Animation animation) {
+ view.setVisibility(View.INVISIBLE);
+ }
+ });
+ }
+ view.startAnimation(animation);
+ } else {
+ view.clearAnimation();
+ view.setVisibility(shown ? View.VISIBLE : View.INVISIBLE);
+ }
+ }
+
+ @Override
+ public RecyclerView onCreateRecyclerView(LayoutInflater inflater, ViewGroup parent,
+ Bundle savedInstanceState) {
+ VerticalGridView verticalGridView = (VerticalGridView) inflater.inflate(
+ R.layout.leanback_preferences_list, parent, false);
+ verticalGridView.setWindowAlignment(VerticalGridView.WINDOW_ALIGN_BOTH_EDGE);
+ verticalGridView.setFocusScrollStrategy(VerticalGridView.FOCUS_SCROLL_ALIGNED);
+ mGridView = verticalGridView;
+ return mGridView;
+ }
+
+ @Override
+ protected RecyclerView.Adapter> onCreateAdapter(PreferenceScreen preferenceScreen) {
+ final RecyclerView.Adapter> adapter = super.onCreateAdapter(preferenceScreen);
+
+ if (adapter != null) {
+ final TextView emptyView = (TextView) getView().findViewById(R.id.no_permissions);
+ onSetEmptyText(emptyView);
+ final RecyclerView recyclerView = getListView();
+ adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
+ @Override
+ public void onChanged() {
+ checkEmpty();
+ }
+
+ @Override
+ public void onItemRangeInserted(int positionStart, int itemCount) {
+ checkEmpty();
+ }
+
+ @Override
+ public void onItemRangeRemoved(int positionStart, int itemCount) {
+ checkEmpty();
+ }
+
+ private void checkEmpty() {
+ boolean isEmpty = isPreferenceListEmpty();
+ emptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
+ recyclerView.setVisibility(isEmpty && adapter.getItemCount() == 0 ?
+ View.GONE : View.VISIBLE);
+ if (!isEmpty && mGridView != null) {
+ mGridView.requestFocus();
+ }
+ }
+ });
+
+ boolean isEmpty = isPreferenceListEmpty();
+ emptyView.setVisibility(isEmpty ? View.VISIBLE : View.GONE);
+ recyclerView.setVisibility(isEmpty && adapter.getItemCount() == 0 ?
+ View.GONE : View.VISIBLE);
+ if (!isEmpty && mGridView != null) {
+ mGridView.requestFocus();
+ }
+ }
+
+ return adapter;
+ }
+
+ private boolean isPreferenceListEmpty() {
+ PreferenceScreen screen = getPreferenceScreen();
+ return screen.getPreferenceCount() == 0 || (
+ screen.getPreferenceCount() == 1 &&
+ (screen.findPreference(HEADER_PREFERENCE_KEY) != null));
+ }
+
+ /**
+ * Hook for subclasses to change the default text of the empty view.
+ * Base implementation leaves the default empty view text.
+ *
+ * @param textView the empty text view
+ */
+ protected void onSetEmptyText(TextView textView) {
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/SettingsWithHeader.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/SettingsWithHeader.java
new file mode 100644
index 0000000000000..06f7c1426af56
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/television/SettingsWithHeader.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.ui.television;
+
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.TextView;
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.R;
+
+public abstract class SettingsWithHeader extends PermissionsFrameFragment
+ implements OnClickListener {
+
+ private View mHeader;
+ protected Intent mInfoIntent;
+ protected Drawable mIcon;
+ protected CharSequence mLabel;
+ protected CharSequence mDecorTitle;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, ViewGroup container,
+ Bundle savedInstanceState) {
+ ViewGroup root = (ViewGroup) super.onCreateView(inflater, container, savedInstanceState);
+
+ mHeader = inflater.inflate(R.layout.header, root, false);
+ getPreferencesContainer().addView(mHeader, 0);
+ updateHeader();
+
+ return root;
+ }
+
+ public void setHeader(Drawable icon, CharSequence label, Intent infoIntent,
+ CharSequence decorTitle) {
+ mIcon = icon;
+ mLabel = label;
+ mInfoIntent = infoIntent;
+ mDecorTitle = decorTitle;
+ updateHeader();
+ }
+
+ public View getHeader() {
+ return mHeader;
+ }
+
+ protected void updateHeader() {
+ final TextView decorTitle = (TextView) mHeader.findViewById(R.id.decor_title);
+ decorTitle.setText(mDecorTitle);
+ }
+
+ @Override
+ public void onClick(View v) {
+ getActivity().startActivity(mInfoIntent);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java
new file mode 100644
index 0000000000000..20495aac24513
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/AppPermissionsFragmentWear.java
@@ -0,0 +1,403 @@
+/*
+* Copyright (C) 2015 The Android Open Source Project
+*
+* Licensed under the Apache License, Version 2.0 (the "License");
+* you may not use this file except in compliance with the License.
+* You may obtain a copy of the License at
+*
+* http://www.apache.org/licenses/LICENSE-2.0
+*
+* Unless required by applicable law or agreed to in writing, software
+* distributed under the License is distributed on an "AS IS" BASIS,
+* WITHOUT 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.packageinstaller.permission.ui.wear;
+
+import android.app.Activity;
+import android.app.Fragment;
+import android.content.DialogInterface;
+import android.content.Intent;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PermissionInfo;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.UserHandle;
+import android.preference.Preference;
+import android.preference.PreferenceFragment;
+import android.preference.PreferenceScreen;
+import android.preference.SwitchPreference;
+import android.util.ArraySet;
+import android.util.Log;
+import android.widget.Toast;
+
+import androidx.wear.ble.view.WearableDialogHelper;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.model.Permission;
+import com.android.packageinstaller.permission.utils.ArrayUtils;
+import com.android.packageinstaller.permission.utils.LocationUtils;
+import com.android.packageinstaller.permission.utils.SafetyNetLogger;
+import com.android.packageinstaller.permission.utils.Utils;
+import com.android.settingslib.RestrictedLockUtils;
+import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class AppPermissionsFragmentWear extends PreferenceFragment {
+ private static final String LOG_TAG = "AppPermFragWear";
+
+ private static final String KEY_NO_PERMISSIONS = "no_permissions";
+
+ public static AppPermissionsFragmentWear newInstance(String packageName) {
+ return setPackageName(new AppPermissionsFragmentWear(), packageName);
+ }
+
+ private static T setPackageName(T fragment, String packageName) {
+ Bundle arguments = new Bundle();
+ arguments.putString(Intent.EXTRA_PACKAGE_NAME, packageName);
+ fragment.setArguments(arguments);
+ return fragment;
+ }
+
+ private PackageManager mPackageManager;
+ private ArraySet mToggledGroups;
+ private AppPermissions mAppPermissions;
+
+ private boolean mHasConfirmedRevoke;
+
+ /**
+ * Provides click behavior for disabled preferences.
+ * We can't use {@link PreferenceFragment#onPreferenceTreeClick}, as the base
+ * {@link SwitchPreference} doesn't delegate to that method if the preference is disabled.
+ */
+ private static class PermissionSwitchPreference extends SwitchPreference {
+
+ private final Activity mActivity;
+
+ public PermissionSwitchPreference(Activity activity) {
+ super(activity);
+ this.mActivity = activity;
+ }
+
+ @Override
+ public void performClick(PreferenceScreen preferenceScreen) {
+ super.performClick(preferenceScreen);
+ if (!isEnabled()) {
+ // If setting the permission is disabled, it must have been locked
+ // by the device or profile owner. So get that info and pass it to
+ // the support details dialog.
+ EnforcedAdmin deviceOrProfileOwner = RestrictedLockUtils.getProfileOrDeviceOwner(
+ mActivity, UserHandle.myUserId());
+ RestrictedLockUtils.sendShowAdminSupportDetailsIntent(
+ mActivity, deviceOrProfileOwner);
+ }
+ }
+ }
+
+ @Override
+ public void onCreate(Bundle savedInstanceState) {
+ super.onCreate(savedInstanceState);
+
+ String packageName = getArguments().getString(Intent.EXTRA_PACKAGE_NAME);
+ Activity activity = getActivity();
+ mPackageManager = activity.getPackageManager();
+
+ PackageInfo packageInfo;
+
+ try {
+ packageInfo = mPackageManager.getPackageInfo(packageName, PackageManager.GET_PERMISSIONS);
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.i(LOG_TAG, "No package:" + activity.getCallingPackage(), e);
+ packageInfo = null;
+ }
+
+ if (packageInfo == null) {
+ Toast.makeText(activity, R.string.app_not_found_dlg_title, Toast.LENGTH_LONG).show();
+ activity.finish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(
+ activity, packageInfo, true, () -> getActivity().finish());
+
+ addPreferencesFromResource(R.xml.watch_permissions);
+ initializePermissionGroupList();
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+
+ // Also refresh the UI
+ for (final AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
+ for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
+ setPreferenceCheckedIfPresent(perm.name,
+ group.areRuntimePermissionsGranted(new String[]{ perm.name }));
+ }
+ } else {
+ setPreferenceCheckedIfPresent(group.getName(), group.areRuntimePermissionsGranted());
+ }
+ }
+ }
+
+ @Override
+ public void onPause() {
+ super.onPause();
+ logAndClearToggledGroups();
+ }
+
+ private void initializePermissionGroupList() {
+ List groups = mAppPermissions.getPermissionGroups();
+ List nonSystemPreferences = new ArrayList<>();
+
+ if (!groups.isEmpty()) {
+ getPreferenceScreen().removePreference(findPreference(KEY_NO_PERMISSIONS));
+ }
+
+ for (final AppPermissionGroup group : groups) {
+ if (!Utils.shouldShowPermission(group)) {
+ continue;
+ }
+
+ boolean isPlatform = group.getDeclaringPackage().equals(Utils.OS_PKG);
+
+ if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())) {
+ // If permission is controlled individually, we show all requested permission
+ // inside this group.
+ for (PermissionInfo perm : getPermissionInfosFromGroup(group)) {
+ final SwitchPreference pref = createSwitchPreferenceForPermission(group, perm);
+ showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
+ }
+ } else {
+ final SwitchPreference pref = createSwitchPreferenceForGroup(group);
+ showOrAddToNonSystemPreferences(pref, nonSystemPreferences, isPlatform);
+ }
+ }
+
+ // Now add the non-system settings to the end of the list
+ for (SwitchPreference nonSystemPreference : nonSystemPreferences) {
+ getPreferenceScreen().addPreference(nonSystemPreference);
+ }
+ }
+
+ private void showOrAddToNonSystemPreferences(SwitchPreference pref,
+ List nonSystemPreferences, // Mutate
+ boolean isPlatform) {
+ // The UI shows System settings first, then non-system settings
+ if (isPlatform) {
+ getPreferenceScreen().addPreference(pref);
+ } else {
+ nonSystemPreferences.add(pref);
+ }
+ }
+
+ private SwitchPreference createSwitchPreferenceForPermission(AppPermissionGroup group,
+ PermissionInfo perm) {
+ final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
+ pref.setKey(perm.name);
+ pref.setTitle(perm.loadLabel(mPackageManager));
+ pref.setChecked(group.areRuntimePermissionsGranted(new String[]{ perm.name }));
+ pref.setOnPreferenceChangeListener((p, newVal) -> {
+ if((Boolean) newVal) {
+ group.grantRuntimePermissions(false, new String[]{ perm.name });
+
+ if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())
+ && group.doesSupportRuntimePermissions()) {
+ // We are granting a permission from a group but since this is an
+ // individual permission control other permissions in the group may
+ // be revoked, hence we need to mark them user fixed to prevent the
+ // app from requesting a non-granted permission and it being granted
+ // because another permission in the group is granted. This applies
+ // only to apps that support runtime permissions.
+ String[] revokedPermissionsToFix = null;
+ final int permissionCount = group.getPermissions().size();
+
+ for (int i = 0; i < permissionCount; i++) {
+ Permission current = group.getPermissions().get(i);
+ if (!current.isGranted() && !current.isUserFixed()) {
+ revokedPermissionsToFix = ArrayUtils.appendString(
+ revokedPermissionsToFix, current.getName());
+ }
+ }
+
+ if (revokedPermissionsToFix != null) {
+ // If some permissions were not granted then they should be fixed.
+ group.revokeRuntimePermissions(true, revokedPermissionsToFix);
+ }
+ }
+ } else {
+ final Permission appPerm = getPermissionFromGroup(group, perm.name);
+ if (appPerm == null) {
+ return false;
+ }
+
+ final boolean grantedByDefault = appPerm.isGrantedByDefault();
+ if (grantedByDefault
+ || (!group.doesSupportRuntimePermissions() && !mHasConfirmedRevoke)) {
+ showRevocationWarningDialog(
+ (dialog, which) -> {
+ revokePermissionInGroup(group, perm.name);
+ pref.setChecked(false);
+ if (!appPerm.isGrantedByDefault()) {
+ mHasConfirmedRevoke = true;
+ }
+ },
+ grantedByDefault
+ ? R.string.system_warning
+ : R.string.old_sdk_deny_warning);
+ return false;
+ } else {
+ revokePermissionInGroup(group, perm.name);
+ }
+ }
+
+ return true;
+ });
+ return pref;
+ }
+
+ private void showRevocationWarningDialog(
+ DialogInterface.OnClickListener confirmListener,
+ int warningMessageId) {
+ new WearableDialogHelper.DialogBuilder(getContext())
+ .setNegativeIcon(R.drawable.confirm_button)
+ .setPositiveIcon(R.drawable.cancel_button)
+ .setNegativeButton(R.string.grant_dialog_button_deny_anyway, confirmListener)
+ .setPositiveButton(R.string.cancel, null)
+ .setMessage(warningMessageId)
+ .show();
+ }
+
+ private static Permission getPermissionFromGroup(AppPermissionGroup group, String permName) {
+ final int permissionCount = group.getPermissions().size();
+
+ for (int i = 0; i < permissionCount; i++) {
+ Permission currentPerm = group.getPermissions().get(i);
+ if(currentPerm.getName().equals(permName)) {
+ return currentPerm;
+ };
+ }
+
+ if ("user".equals(Build.TYPE)) {
+ Log.e(LOG_TAG, String.format("The impossible happens, permission %s is not in group %s.",
+ permName, group.getName()));
+ return null;
+ } else {
+ // This is impossible, throw a fatal error in non-user build.
+ throw new IllegalArgumentException(
+ String.format("Permission %s is not in group %s", permName, group.getName()));
+ }
+ }
+
+ private void revokePermissionInGroup(AppPermissionGroup group, String permName) {
+ group.revokeRuntimePermissions(true, new String[]{ permName });
+
+ if (Utils.areGroupPermissionsIndividuallyControlled(getContext(), group.getName())
+ && group.doesSupportRuntimePermissions()
+ && !group.areRuntimePermissionsGranted()) {
+ // If we just revoked the last permission we need to clear
+ // the user fixed state as now the app should be able to
+ // request them at runtime if supported.
+ group.revokeRuntimePermissions(false);
+ }
+ }
+
+ private SwitchPreference createSwitchPreferenceForGroup(AppPermissionGroup group) {
+ final SwitchPreference pref = new PermissionSwitchPreference(getActivity());
+
+ pref.setKey(group.getName());
+ pref.setTitle(group.getLabel());
+ pref.setChecked(group.areRuntimePermissionsGranted());
+
+ if (group.isPolicyFixed()) {
+ pref.setEnabled(false);
+ } else {
+ pref.setOnPreferenceChangeListener((p, newVal) -> {
+ if (LocationUtils.isLocationGroupAndProvider(
+ group.getName(), group.getApp().packageName)) {
+ LocationUtils.showLocationDialog(
+ getContext(), mAppPermissions.getAppLabel());
+ return false;
+ }
+
+ if ((Boolean) newVal) {
+ setPermission(group, pref, true);
+ } else {
+ final boolean grantedByDefault = group.hasGrantedByDefaultPermission();
+ if (grantedByDefault
+ || (!group.doesSupportRuntimePermissions() && !mHasConfirmedRevoke)) {
+ showRevocationWarningDialog(
+ (dialog, which) -> {
+ setPermission(group, pref, false);
+ if (!group.hasGrantedByDefaultPermission()) {
+ mHasConfirmedRevoke = true;
+ }
+ },
+ grantedByDefault
+ ? R.string.system_warning
+ : R.string.old_sdk_deny_warning);
+ return false;
+ } else {
+ setPermission(group, pref, false);
+ }
+ }
+
+ return true;
+ });
+ }
+ return pref;
+ }
+
+ private void setPermission(AppPermissionGroup group, SwitchPreference pref, boolean grant) {
+ if (grant) {
+ group.grantRuntimePermissions(false);
+ } else {
+ group.revokeRuntimePermissions(false);
+ }
+ addToggledGroup(group);
+ pref.setChecked(grant);
+ }
+
+ private void addToggledGroup(AppPermissionGroup group) {
+ if (mToggledGroups == null) {
+ mToggledGroups = new ArraySet<>();
+ }
+
+ mToggledGroups.add(group);
+ }
+
+ private void logAndClearToggledGroups() {
+ if (mToggledGroups != null) {
+ SafetyNetLogger.logPermissionsToggled(mToggledGroups);
+ mToggledGroups = null;
+ }
+ }
+
+ private List getPermissionInfosFromGroup(AppPermissionGroup group) {
+ ArrayList permInfos = new ArrayList<>(group.getPermissions().size());
+ for(Permission perm : group.getPermissions()) {
+ try {
+ permInfos.add(mPackageManager.getPermissionInfo(perm.getName(), 0));
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.w(LOG_TAG, "No permission:" + perm.getName());
+ }
+ }
+ return permInfos;
+ }
+
+ private void setPreferenceCheckedIfPresent(String preferenceKey, boolean checked) {
+ Preference pref = findPreference(preferenceKey);
+ if (pref instanceof SwitchPreference) {
+ ((SwitchPreference) pref).setChecked(checked);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/ReviewPermissionsWearFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/ReviewPermissionsWearFragment.java
new file mode 100644
index 0000000000000..5aa7cafec8a82
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/ui/wear/ReviewPermissionsWearFragment.java
@@ -0,0 +1,357 @@
+/*
+ * Copyright (C) 2017 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.ui.wear;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.content.IntentSender;
+import android.content.pm.PackageInfo;
+import android.graphics.drawable.Drawable;
+import android.os.Bundle;
+import android.os.RemoteCallback;
+import android.text.SpannableString;
+import android.text.style.ForegroundColorSpan;
+import android.util.Log;
+import android.util.TypedValue;
+
+import androidx.preference.Preference;
+import androidx.preference.PreferenceCategory;
+import androidx.preference.PreferenceFragmentCompat;
+import androidx.preference.PreferenceGroup;
+import androidx.preference.PreferenceScreen;
+import androidx.preference.SwitchPreference;
+import androidx.preference.TwoStatePreference;
+import androidx.wear.ble.view.WearableDialogHelper;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.utils.Utils;
+
+import java.util.List;
+
+public class ReviewPermissionsWearFragment extends PreferenceFragmentCompat
+ implements Preference.OnPreferenceChangeListener {
+ private static final String TAG = "ReviewPermWear";
+
+ private static final int ORDER_NEW_PERMS = 1;
+ private static final int ORDER_CURRENT_PERMS = 2;
+ // Category for showing actions should be displayed last.
+ private static final int ORDER_ACTION = 100000;
+ private static final int ORDER_PERM_OFFSET_START = 100;
+
+ private static final String EXTRA_PACKAGE_INFO =
+ "com.android.packageinstaller.permission.ui.extra.PACKAGE_INFO";
+
+ public static ReviewPermissionsWearFragment newInstance(PackageInfo packageInfo) {
+ Bundle arguments = new Bundle();
+ arguments.putParcelable(EXTRA_PACKAGE_INFO, packageInfo);
+ ReviewPermissionsWearFragment instance = new ReviewPermissionsWearFragment();
+ instance.setArguments(arguments);
+ instance.setRetainInstance(true);
+ return instance;
+ }
+
+ private AppPermissions mAppPermissions;
+
+ private PreferenceCategory mNewPermissionsCategory;
+
+ private boolean mHasConfirmedRevoke;
+
+ @Override
+ public void onCreatePreferences(Bundle savedInstanceState, String rootKey) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ PackageInfo packageInfo = getArguments().getParcelable(EXTRA_PACKAGE_INFO);
+ if (packageInfo == null) {
+ activity.finish();
+ return;
+ }
+
+ mAppPermissions = new AppPermissions(activity, packageInfo, false,
+ () -> getActivity().finish());
+
+ if (mAppPermissions.getPermissionGroups().isEmpty()) {
+ activity.finish();
+ return;
+ }
+
+ boolean reviewRequired = false;
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (group.isReviewRequired()) {
+ reviewRequired = true;
+ break;
+ }
+ }
+
+ if (!reviewRequired) {
+ activity.finish();
+ }
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ mAppPermissions.refresh();
+ loadPreferences();
+ }
+
+ private void loadPreferences() {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+
+ PreferenceScreen screen = getPreferenceScreen();
+ if (screen == null) {
+ screen = getPreferenceManager().createPreferenceScreen(getActivity());
+ setPreferenceScreen(screen);
+ } else {
+ screen.removeAll();
+ }
+
+ PreferenceGroup currentPermissionsCategory = null;
+ PreferenceGroup oldNewPermissionsCategory = mNewPermissionsCategory;
+ mNewPermissionsCategory = null;
+
+ final boolean isPackageUpdated = isPackageUpdated();
+ int permOrder = ORDER_PERM_OFFSET_START;
+
+ for (AppPermissionGroup group : mAppPermissions.getPermissionGroups()) {
+ if (!Utils.shouldShowPermission(group)
+ || !Utils.OS_PKG.equals(group.getDeclaringPackage())) {
+ continue;
+ }
+
+ final SwitchPreference preference;
+ Preference cachedPreference = oldNewPermissionsCategory != null
+ ? oldNewPermissionsCategory.findPreference(group.getName()) : null;
+ if (cachedPreference instanceof SwitchPreference) {
+ preference = (SwitchPreference) cachedPreference;
+ } else {
+ preference = new SwitchPreference(getActivity());
+
+ preference.setKey(group.getName());
+ preference.setTitle(group.getLabel());
+ preference.setPersistent(false);
+ preference.setOrder(permOrder++);
+
+ preference.setOnPreferenceChangeListener(this);
+ }
+
+ preference.setChecked(group.areRuntimePermissionsGranted());
+
+ // Mutable state
+ if (group.isPolicyFixed()) {
+ preference.setEnabled(false);
+ } else {
+ preference.setEnabled(true);
+ }
+
+ if (group.isReviewRequired()) {
+ if (!isPackageUpdated) {
+ // An app just being installed, which means all groups requiring reviews.
+ screen.addPreference(preference);
+ } else {
+ if (mNewPermissionsCategory == null) {
+ mNewPermissionsCategory = new PreferenceCategory(activity);
+ mNewPermissionsCategory.setTitle(R.string.new_permissions_category);
+ mNewPermissionsCategory.setOrder(ORDER_NEW_PERMS);
+ screen.addPreference(mNewPermissionsCategory);
+ }
+ mNewPermissionsCategory.addPreference(preference);
+ }
+ } else {
+ if (currentPermissionsCategory == null) {
+ currentPermissionsCategory = new PreferenceCategory(activity);
+ currentPermissionsCategory.setTitle(R.string.current_permissions_category);
+ currentPermissionsCategory.setOrder(ORDER_CURRENT_PERMS);
+ screen.addPreference(currentPermissionsCategory);
+ }
+ currentPermissionsCategory.addPreference(preference);
+ }
+ }
+
+ addTitlePreferenceToScreen(screen);
+ addActionPreferencesToScreen(screen);
+ }
+
+ private boolean isPackageUpdated() {
+ List groups = mAppPermissions.getPermissionGroups();
+ final int groupCount = groups.size();
+ for (int i = 0; i < groupCount; i++) {
+ AppPermissionGroup group = groups.get(i);
+ if (!group.isReviewRequired()) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public boolean onPreferenceChange(Preference preference, Object newValue) {
+ Log.d(TAG, "onPreferenceChange " + preference.getTitle());
+ if (mHasConfirmedRevoke) {
+ return true;
+ }
+ if (preference instanceof SwitchPreference) {
+ SwitchPreference switchPreference = (SwitchPreference) preference;
+ if (switchPreference.isChecked()) {
+ showWarnRevokeDialog(switchPreference);
+ } else {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ private void showWarnRevokeDialog(final SwitchPreference preference) {
+ // When revoking, we set "confirm" as the negative icon to be shown at the bottom.
+ new WearableDialogHelper.DialogBuilder(getContext())
+ .setPositiveIcon(R.drawable.cancel_button)
+ .setNegativeIcon(R.drawable.confirm_button)
+ .setPositiveButton(R.string.cancel, null)
+ .setNegativeButton(R.string.grant_dialog_button_deny_anyway,
+ (dialog, which) -> {
+ preference.setChecked(false);
+ mHasConfirmedRevoke = true;
+ })
+ .setMessage(R.string.old_sdk_deny_warning)
+ .show();
+ }
+
+ private void confirmPermissionsReview() {
+ PreferenceGroup preferenceGroup = mNewPermissionsCategory != null
+ ? mNewPermissionsCategory : getPreferenceScreen();
+
+ final int preferenceCount = preferenceGroup.getPreferenceCount();
+ for (int i = 0; i < preferenceCount; i++) {
+ Preference preference = preferenceGroup.getPreference(i);
+ if (preference instanceof TwoStatePreference) {
+ TwoStatePreference twoStatePreference = (TwoStatePreference) preference;
+ String groupName = preference.getKey();
+ AppPermissionGroup group = mAppPermissions.getPermissionGroup(groupName);
+ if (twoStatePreference.isChecked()) {
+ group.grantRuntimePermissions(false);
+ } else {
+ group.revokeRuntimePermissions(false);
+ }
+ group.resetReviewRequired();
+ }
+ }
+ }
+
+ private void addTitlePreferenceToScreen(PreferenceScreen screen) {
+ Activity activity = getActivity();
+ Preference titlePref = new Preference(activity);
+ screen.addPreference(titlePref);
+
+ // Set icon
+ Drawable icon = mAppPermissions.getPackageInfo().applicationInfo.loadIcon(
+ activity.getPackageManager());
+ titlePref.setIcon(icon);
+
+ // Set message
+ String appLabel = mAppPermissions.getAppLabel().toString();
+ final int labelTemplateResId = isPackageUpdated()
+ ? R.string.permission_review_title_template_update
+ : R.string.permission_review_title_template_install;
+ SpannableString message = new SpannableString(getString(labelTemplateResId, appLabel));
+
+ // Color the app name.
+ final int appLabelStart = message.toString().indexOf(appLabel, 0);
+ final int appLabelLength = appLabel.length();
+
+ TypedValue typedValue = new TypedValue();
+ activity.getTheme().resolveAttribute(android.R.attr.colorAccent, typedValue, true);
+ final int color = activity.getColor(typedValue.resourceId);
+
+ message.setSpan(new ForegroundColorSpan(color), appLabelStart,
+ appLabelStart + appLabelLength, 0);
+
+ titlePref.setTitle(message);
+
+ titlePref.setSelectable(false);
+ titlePref.setLayoutResource(R.layout.wear_review_permission_title_pref);
+ }
+
+ private void addActionPreferencesToScreen(PreferenceScreen screen) {
+ final Activity activity = getActivity();
+
+ Preference cancelPref = new Preference(activity);
+ cancelPref.setTitle(R.string.review_button_cancel);
+ cancelPref.setOrder(ORDER_ACTION);
+ cancelPref.setEnabled(true);
+ cancelPref.setLayoutResource(R.layout.wear_review_permission_action_pref);
+ cancelPref.setOnPreferenceClickListener(p -> {
+ executeCallback(false);
+ activity.setResult(Activity.RESULT_CANCELED);
+ activity.finish();
+ return true;
+ });
+ screen.addPreference(cancelPref);
+
+ Preference continuePref = new Preference(activity);
+ continuePref.setTitle(R.string.review_button_continue);
+ continuePref.setOrder(ORDER_ACTION + 1);
+ continuePref.setEnabled(true);
+ continuePref.setLayoutResource(R.layout.wear_review_permission_action_pref);
+ continuePref.setOnPreferenceClickListener(p -> {
+ confirmPermissionsReview();
+ executeCallback(true);
+ getActivity().finish();
+ return true;
+ });
+ screen.addPreference(continuePref);
+ }
+
+ private void executeCallback(boolean success) {
+ Activity activity = getActivity();
+ if (activity == null) {
+ return;
+ }
+ if (success) {
+ IntentSender intent = activity.getIntent().getParcelableExtra(Intent.EXTRA_INTENT);
+ if (intent != null) {
+ try {
+ int flagMask = 0;
+ int flagValues = 0;
+ if (activity.getIntent().getBooleanExtra(
+ Intent.EXTRA_RESULT_NEEDED, false)) {
+ flagMask = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
+ flagValues = Intent.FLAG_ACTIVITY_FORWARD_RESULT;
+ }
+ activity.startIntentSenderForResult(intent, -1, null,
+ flagMask, flagValues, 0);
+ } catch (IntentSender.SendIntentException e) {
+ /* ignore */
+ }
+ return;
+ }
+ }
+ RemoteCallback callback = activity.getIntent().getParcelableExtra(
+ Intent.EXTRA_REMOTE_CALLBACK);
+ if (callback != null) {
+ Bundle result = new Bundle();
+ result.putBoolean(Intent.EXTRA_RETURN_RESULT, success);
+ callback.sendResult(result);
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/ArrayUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/ArrayUtils.java
new file mode 100644
index 0000000000000..2af641bc321c2
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/ArrayUtils.java
@@ -0,0 +1,63 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.utils;
+
+import android.text.TextUtils;
+
+import java.util.Objects;
+
+public final class ArrayUtils {
+ private ArrayUtils() { /* cannot be instantiated */ }
+
+ /**
+ * Checks that value is present as at least one of the elements of the array.
+ * @param array the array to check in
+ * @param value the value to check for
+ * @return true if the value is present in the array
+ */
+ public static boolean contains(T[] array, T value) {
+ return indexOf(array, value) != -1;
+ }
+
+ /**
+ * Return first index of {@code value} in {@code array}, or {@code -1} if
+ * not found.
+ */
+ public static int indexOf(T[] array, T value) {
+ if (array == null) return -1;
+ for (int i = 0; i < array.length; i++) {
+ if (Objects.equals(array[i], value)) return i;
+ }
+ return -1;
+ }
+
+ public static String[] appendString(String[] cur, String val) {
+ if (cur == null) {
+ return new String[] { val };
+ }
+ final int N = cur.length;
+ for (int i = 0; i < N; i++) {
+ if (TextUtils.equals(cur[i], val)) {
+ return cur;
+ }
+ }
+ String[] ret = new String[N + 1];
+ System.arraycopy(cur, 0, ret, 0, N);
+ ret[N] = val;
+ return ret;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/EventLogger.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/EventLogger.java
new file mode 100644
index 0000000000000..7aff7ada792d5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/EventLogger.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.utils;
+
+import android.metrics.LogMaker;
+import androidx.annotation.NonNull;
+
+import com.android.internal.logging.MetricsLogger;
+import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
+
+public class EventLogger {
+ private static final MetricsLogger sMetricsLogger = new MetricsLogger();
+
+ /**
+ * Log that a permission was requested/denied.
+ *
+ * @param action the action performed
+ * @param name name of the permission
+ * @param packageName package permission is for
+ */
+ public static void logPermission(int action, @NonNull String name,
+ @NonNull String packageName) {
+ final LogMaker log = new LogMaker(action);
+ log.setPackageName(packageName);
+ log.addTaggedData(MetricsEvent.FIELD_PERMISSION, name);
+
+ sMetricsLogger.write(log);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/IoUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/IoUtils.java
new file mode 100644
index 0000000000000..ff7d1831519f9
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/IoUtils.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.packageinstaller.permission.utils;
+
+public final class IoUtils {
+ private IoUtils() {
+ }
+
+ /**
+ * Closes 'closeable', ignoring any checked exceptions. Does nothing if 'closeable' is null.
+ */
+ public static void closeQuietly(AutoCloseable closeable) {
+ if (closeable != null) {
+ try {
+ closeable.close();
+ } catch (RuntimeException rethrown) {
+ throw rethrown;
+ } catch (Exception ignored) {
+ }
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/LocationUtils.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/LocationUtils.java
new file mode 100644
index 0000000000000..0296ae80ad4c7
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/LocationUtils.java
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.utils;
+
+import android.Manifest;
+import android.app.AlertDialog;
+import android.content.Context;
+import android.content.DialogInterface;
+import android.content.DialogInterface.OnClickListener;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.location.ILocationManager;
+import android.location.LocationManager;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.provider.Settings;
+
+import com.android.packageinstaller.R;
+
+import java.util.ArrayList;
+
+public class LocationUtils {
+
+ public static final String LOCATION_PERMISSION = Manifest.permission_group.LOCATION;
+
+ public static void showLocationDialog(final Context context, CharSequence label) {
+ new AlertDialog.Builder(context)
+ .setIcon(R.drawable.ic_dialog_alert_material)
+ .setTitle(android.R.string.dialog_alert_title)
+ .setMessage(context.getString(R.string.location_warning, label))
+ .setNegativeButton(R.string.ok, null)
+ .setPositiveButton(R.string.location_settings, new OnClickListener() {
+ @Override
+ public void onClick(DialogInterface dialog, int which) {
+ context.startActivity(new Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS));
+ }
+ })
+ .show();
+ }
+
+ public static boolean isLocationEnabled(Context context) {
+ return Settings.Secure.getInt(context.getContentResolver(), Settings.Secure.LOCATION_MODE,
+ Settings.Secure.LOCATION_MODE_OFF) != Settings.Secure.LOCATION_MODE_OFF;
+ }
+
+ public static boolean isLocationGroupAndProvider(String groupName, String packageName) {
+ return LOCATION_PERMISSION.equals(groupName) && isNetworkLocationProvider(packageName);
+ }
+
+ private static boolean isNetworkLocationProvider(String packageName) {
+ ILocationManager locationService = ILocationManager.Stub.asInterface(
+ ServiceManager.getService(Context.LOCATION_SERVICE));
+ try {
+ return packageName.equals(locationService.getNetworkProviderPackage());
+ } catch (RemoteException e) {
+ return false;
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/SafetyNetLogger.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/SafetyNetLogger.java
new file mode 100644
index 0000000000000..7773294b0998c
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/SafetyNetLogger.java
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.utils;
+
+import android.content.pm.PackageInfo;
+import android.util.ArrayMap;
+import android.util.ArraySet;
+import android.util.EventLog;
+
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.Permission;
+
+import java.util.ArrayList;
+import java.util.List;
+
+public final class SafetyNetLogger {
+
+ // The log tag used by SafetyNet to pick entries from the event log.
+ private static final int SNET_NET_EVENT_LOG_TAG = 0x534e4554;
+
+ // Log tag for the result of permissions request.
+ private static final String PERMISSIONS_REQUESTED = "individual_permissions_requested";
+
+ // Log tag for the result of permissions toggling.
+ private static final String PERMISSIONS_TOGGLED = "individual_permissions_toggled";
+
+ private SafetyNetLogger() {
+ /* do nothing */
+ }
+
+ public static void logPermissionsRequested(PackageInfo packageInfo,
+ List groups) {
+ EventLog.writeEvent(SNET_NET_EVENT_LOG_TAG, PERMISSIONS_REQUESTED,
+ packageInfo.applicationInfo.uid, buildChangedPermissionForPackageMessage(
+ packageInfo.packageName, groups));
+ }
+
+ /**
+ * Log that permission groups have been toggled for the purpose of safety net.
+ *
+ *
The groups might refer to different permission groups and different apps.
+ *
+ * @param groups The groups toggled
+ */
+ public static void logPermissionsToggled(ArraySet groups) {
+ ArrayMap> groupsByPackage = new ArrayMap<>();
+
+ int numGroups = groups.size();
+ for (int i = 0; i < numGroups; i++) {
+ AppPermissionGroup group = groups.valueAt(i);
+
+ ArrayList groupsForThisPackage = groupsByPackage.get(
+ group.getApp().packageName);
+ if (groupsForThisPackage == null) {
+ groupsForThisPackage = new ArrayList<>();
+ groupsByPackage.put(group.getApp().packageName, groupsForThisPackage);
+ }
+
+ groupsForThisPackage.add(group);
+ if (group.getBackgroundPermissions() != null) {
+ groupsForThisPackage.add(group.getBackgroundPermissions());
+ }
+ }
+
+ int numPackages = groupsByPackage.size();
+ for (int i = 0; i < numPackages; i++) {
+ EventLog.writeEvent(SNET_NET_EVENT_LOG_TAG, PERMISSIONS_TOGGLED,
+ android.os.Process.myUid(), buildChangedPermissionForPackageMessage(
+ groupsByPackage.keyAt(i), groupsByPackage.valueAt(i)));
+ }
+ }
+
+ private static void buildChangedPermissionForGroup(AppPermissionGroup group,
+ StringBuilder builder) {
+ int permissionCount = group.getPermissions().size();
+ for (int permissionNum = 0; permissionNum < permissionCount; permissionNum++) {
+ Permission permission = group.getPermissions().get(permissionNum);
+
+ if (builder.length() > 0) {
+ builder.append(';');
+ }
+
+ builder.append(permission.getName()).append('|');
+
+ if (group.doesSupportRuntimePermissions()) {
+ builder.append(permission.isGranted()).append('|');
+ } else {
+ builder.append(permission.isGranted()
+ && (!permission.affectsAppOp() || permission.isAppOpAllowed())).append('|');
+ }
+
+ builder.append(permission.getFlags());
+ }
+ }
+
+ private static String buildChangedPermissionForPackageMessage(String packageName,
+ List groups) {
+ StringBuilder builder = new StringBuilder();
+
+ builder.append(packageName).append(':');
+
+ int groupCount = groups.size();
+ for (int groupNum = 0; groupNum < groupCount; groupNum++) {
+ AppPermissionGroup group = groups.get(groupNum);
+
+ buildChangedPermissionForGroup(group, builder);
+ if (group.getBackgroundPermissions() != null) {
+ buildChangedPermissionForGroup(group.getBackgroundPermissions(), builder);
+ }
+ }
+
+ return builder.toString();
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/Utils.java b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/Utils.java
new file mode 100644
index 0000000000000..d7ee219b0e470
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/permission/utils/Utils.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.permission.utils;
+
+import android.Manifest;
+import android.annotation.StringRes;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.res.Resources;
+import android.content.res.Resources.Theme;
+import android.graphics.drawable.Drawable;
+import android.text.Html;
+import android.util.ArraySet;
+import android.util.Log;
+import android.util.TypedValue;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.permission.model.AppPermissionGroup;
+import com.android.packageinstaller.permission.model.AppPermissions;
+import com.android.packageinstaller.permission.model.PermissionApps.PermissionApp;
+
+import java.util.List;
+
+public final class Utils {
+
+ private static final String LOG_TAG = "Utils";
+
+ public static final String OS_PKG = "android";
+
+ public static final String[] MODERN_PERMISSION_GROUPS = {
+ Manifest.permission_group.CALENDAR,
+ Manifest.permission_group.CALL_LOG,
+ Manifest.permission_group.CAMERA,
+ Manifest.permission_group.CONTACTS,
+ Manifest.permission_group.LOCATION,
+ Manifest.permission_group.SENSORS,
+ Manifest.permission_group.SMS,
+ Manifest.permission_group.PHONE,
+ Manifest.permission_group.MICROPHONE,
+ Manifest.permission_group.STORAGE
+ };
+
+ private static final Intent LAUNCHER_INTENT = new Intent(Intent.ACTION_MAIN, null)
+ .addCategory(Intent.CATEGORY_LAUNCHER);
+
+ private Utils() {
+ /* do nothing - hide constructor */
+ }
+
+ public static Drawable loadDrawable(PackageManager pm, String pkg, int resId) {
+ try {
+ return pm.getResourcesForApplication(pkg).getDrawable(resId, null);
+ } catch (Resources.NotFoundException | PackageManager.NameNotFoundException e) {
+ Log.d(LOG_TAG, "Couldn't get resource", e);
+ return null;
+ }
+ }
+
+ public static boolean isModernPermissionGroup(String name) {
+ for (String modernGroup : MODERN_PERMISSION_GROUPS) {
+ if (modernGroup.equals(name)) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Should UI show this permission.
+ *
+ *
If the user cannot change the group, it should not be shown.
+ *
+ * @param group The group that might need to be shown to the user
+ *
+ * @return
+ */
+ public static boolean shouldShowPermission(AppPermissionGroup group) {
+ boolean isSystemFixed = group.isSystemFixed();
+ if (group.getBackgroundPermissions() != null) {
+ // If the foreground mode is fixed to "enabled", the background mode might still be
+ // switchable. We only want to suppress the group if nothing can be switched
+ if (group.areRuntimePermissionsGranted()) {
+ isSystemFixed &= group.getBackgroundPermissions().isSystemFixed();
+ }
+ }
+
+ // We currently will not show permissions fixed by the system.
+ // which is what the system does for system components.
+ if (isSystemFixed && !LocationUtils.isLocationGroupAndProvider(
+ group.getName(), group.getApp().packageName)) {
+ return false;
+ }
+
+ if (!group.isGrantingAllowed()) {
+ return false;
+ }
+
+ final boolean isPlatformPermission = group.getDeclaringPackage().equals(OS_PKG);
+ // Show legacy permissions only if the user chose that.
+ if (isPlatformPermission
+ && !Utils.isModernPermissionGroup(group.getName())) {
+ return false;
+ }
+ return true;
+ }
+
+ public static Drawable applyTint(Context context, Drawable icon, int attr) {
+ Theme theme = context.getTheme();
+ TypedValue typedValue = new TypedValue();
+ theme.resolveAttribute(attr, typedValue, true);
+ icon = icon.mutate();
+ icon.setTint(context.getColor(typedValue.resourceId));
+ return icon;
+ }
+
+ public static Drawable applyTint(Context context, int iconResId, int attr) {
+ return applyTint(context, context.getDrawable(iconResId), attr);
+ }
+
+ public static ArraySet getLauncherPackages(Context context) {
+ ArraySet launcherPkgs = new ArraySet<>();
+ for (ResolveInfo info :
+ context.getPackageManager().queryIntentActivities(LAUNCHER_INTENT, 0)) {
+ launcherPkgs.add(info.activityInfo.packageName);
+ }
+
+ return launcherPkgs;
+ }
+
+ public static List getAllInstalledApplications(Context context) {
+ return context.getPackageManager().getInstalledApplications(0);
+ }
+
+ public static boolean isSystem(PermissionApp app, ArraySet launcherPkgs) {
+ return isSystem(app.getAppInfo(), launcherPkgs);
+ }
+
+ public static boolean isSystem(AppPermissions app, ArraySet launcherPkgs) {
+ return isSystem(app.getPackageInfo().applicationInfo, launcherPkgs);
+ }
+
+ public static boolean isSystem(ApplicationInfo info, ArraySet launcherPkgs) {
+ return info.isSystemApp() && (info.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) == 0
+ && !launcherPkgs.contains(info.packageName);
+ }
+
+ public static boolean areGroupPermissionsIndividuallyControlled(Context context, String group) {
+ if (!context.getPackageManager().arePermissionsIndividuallyControlled()) {
+ return false;
+ }
+ return Manifest.permission_group.SMS.equals(group)
+ || Manifest.permission_group.PHONE.equals(group)
+ || Manifest.permission_group.CONTACTS.equals(group);
+ }
+
+ public static boolean isPermissionIndividuallyControlled(Context context, String permission) {
+ if (!context.getPackageManager().arePermissionsIndividuallyControlled()) {
+ return false;
+ }
+ return Manifest.permission.READ_CONTACTS.equals(permission)
+ || Manifest.permission.WRITE_CONTACTS.equals(permission)
+ || Manifest.permission.SEND_SMS.equals(permission)
+ || Manifest.permission.RECEIVE_SMS.equals(permission)
+ || Manifest.permission.READ_SMS.equals(permission)
+ || Manifest.permission.RECEIVE_MMS.equals(permission)
+ || Manifest.permission.CALL_PHONE.equals(permission)
+ || Manifest.permission.READ_CALL_LOG.equals(permission)
+ || Manifest.permission.WRITE_CALL_LOG.equals(permission);
+ }
+
+ /**
+ * Get the message shown to grant a permission group to an app.
+ *
+ * @param appLabel The label of the app
+ * @param group the group to be granted
+ * @param context A context to resolve resources
+ * @param requestRes The resource id of the grant request message
+ *
+ * @return The formatted message to be used as title when granting permissions
+ */
+ public static CharSequence getRequestMessage(CharSequence appLabel, AppPermissionGroup group,
+ Context context, @StringRes int requestRes) {
+ if (requestRes != 0) {
+ try {
+ return Html.fromHtml(context.getPackageManager().getResourcesForApplication(
+ group.getDeclaringPackage()).getString(requestRes, appLabel), 0);
+ } catch (PackageManager.NameNotFoundException ignored) {
+ }
+ }
+
+ return Html.fromHtml(context.getString(R.string.permission_warning_template, appLabel,
+ group.getDescription()), 0);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java
new file mode 100644
index 0000000000000..f00f68445c187
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/ErrorFragment.java
@@ -0,0 +1,66 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.app.Activity;
+import android.os.Bundle;
+import androidx.leanback.app.GuidedStepFragment;
+import androidx.leanback.widget.GuidanceStylist;
+import androidx.leanback.widget.GuidedAction;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+import java.util.List;
+
+public class ErrorFragment extends GuidedStepFragment {
+ public static final String TITLE = "com.android.packageinstaller.arg.title";
+ public static final String TEXT = "com.android.packageinstaller.arg.text";
+
+ @Override
+ public int onProvideTheme() {
+ return R.style.Theme_Leanback_GuidedStep;
+ }
+
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ return new GuidanceStylist.Guidance(
+ getString(getArguments().getInt(TITLE)),
+ getString(getArguments().getInt(TEXT)),
+ null,
+ null);
+ }
+
+ @Override
+ public void onCreateActions(List actions, Bundle savedInstanceState) {
+ actions.add(new GuidedAction.Builder(getContext())
+ .clickAction(GuidedAction.ACTION_ID_OK)
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (isAdded()) {
+ if (getActivity() instanceof UninstallerActivity) {
+ ((UninstallerActivity) getActivity()).dispatchAborted();
+ }
+
+ getActivity().setResult(Activity.RESULT_FIRST_USER);
+ getActivity().finish();
+ }
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
new file mode 100644
index 0000000000000..828e5dbcfe5bb
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAlertFragment.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.app.Activity;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.os.Bundle;
+import android.os.UserManager;
+import androidx.leanback.app.GuidedStepFragment;
+import androidx.leanback.widget.GuidanceStylist;
+import androidx.leanback.widget.GuidedAction;
+
+import com.android.packageinstaller.R;
+import com.android.packageinstaller.UninstallerActivity;
+
+import java.util.List;
+
+public class UninstallAlertFragment extends GuidedStepFragment {
+ @Override
+ public int onProvideTheme() {
+ return R.style.Theme_Leanback_GuidedStep;
+ }
+
+ @Override
+ public GuidanceStylist.Guidance onCreateGuidance(Bundle savedInstanceState) {
+ final PackageManager pm = getActivity().getPackageManager();
+ final UninstallerActivity.DialogInfo dialogInfo =
+ ((UninstallerActivity) getActivity()).getDialogInfo();
+ final CharSequence appLabel = dialogInfo.appInfo.loadSafeLabel(pm);
+
+ StringBuilder messageBuilder = new StringBuilder();
+
+ // If the Activity label differs from the App label, then make sure the user
+ // knows the Activity belongs to the App being uninstalled.
+ if (dialogInfo.activityInfo != null) {
+ final CharSequence activityLabel = dialogInfo.activityInfo.loadSafeLabel(pm);
+ if (!activityLabel.equals(appLabel)) {
+ messageBuilder.append(
+ getString(R.string.uninstall_activity_text, activityLabel));
+ messageBuilder.append(" ").append(appLabel).append(".\n\n");
+ }
+ }
+
+ final boolean isUpdate =
+ ((dialogInfo.appInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ UserManager userManager = UserManager.get(getActivity());
+ if (isUpdate) {
+ if (isSingleUser(userManager)) {
+ messageBuilder.append(getString(R.string.uninstall_update_text));
+ } else {
+ messageBuilder.append(getString(R.string.uninstall_update_text_multiuser));
+ }
+ } else {
+ if (dialogInfo.allUsers && !isSingleUser(userManager)) {
+ messageBuilder.append(getString(R.string.uninstall_application_text_all_users));
+ } else if (!dialogInfo.user.equals(android.os.Process.myUserHandle())) {
+ UserInfo userInfo = userManager.getUserInfo(dialogInfo.user.getIdentifier());
+ messageBuilder.append(
+ getString(R.string.uninstall_application_text_user, userInfo.name));
+ } else {
+ messageBuilder.append(getString(R.string.uninstall_application_text));
+ }
+ }
+
+ return new GuidanceStylist.Guidance(
+ appLabel.toString(),
+ messageBuilder.toString(),
+ null,
+ dialogInfo.appInfo.loadIcon(pm));
+ }
+
+ @Override
+ public void onCreateActions(List actions, Bundle savedInstanceState) {
+ actions.add(new GuidedAction.Builder(getContext())
+ .clickAction(GuidedAction.ACTION_ID_OK)
+ .build());
+ actions.add(new GuidedAction.Builder(getContext())
+ .clickAction(GuidedAction.ACTION_ID_CANCEL)
+ .build());
+ }
+
+ @Override
+ public void onGuidedActionClicked(GuidedAction action) {
+ if (isAdded()) {
+ if (action.getId() == GuidedAction.ACTION_ID_OK) {
+ ((UninstallerActivity) getActivity()).startUninstallProgress();
+ getActivity().finish();
+ } else {
+ ((UninstallerActivity) getActivity()).dispatchAborted();
+ getActivity().setResult(Activity.RESULT_FIRST_USER);
+ getActivity().finish();
+ }
+ }
+ }
+
+ /**
+ * Returns whether there is only one user on this device, not including
+ * the system-only user.
+ */
+ private boolean isSingleUser(UserManager userManager) {
+ final int userCount = userManager.getUserCount();
+ return userCount == 1
+ || (UserManager.isSplitSystemUser() && userCount == 2);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java
new file mode 100755
index 0000000000000..a4f217c4a5e3c
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgress.java
@@ -0,0 +1,377 @@
+/*
+**
+** Copyright 2007, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+** http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT 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.packageinstaller.television;
+
+import android.app.Activity;
+import android.app.admin.IDevicePolicyManager;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.IPackageDeleteObserver2;
+import android.content.pm.IPackageManager;
+import android.content.pm.PackageInstaller;
+import android.content.pm.PackageManager;
+import android.content.pm.UserInfo;
+import android.graphics.Color;
+import android.graphics.drawable.ColorDrawable;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.UserHandle;
+import android.os.UserManager;
+import android.util.Log;
+import android.util.TypedValue;
+import android.view.KeyEvent;
+import android.widget.Toast;
+
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+import java.lang.ref.WeakReference;
+import java.util.List;
+
+/**
+ * This activity corresponds to a download progress screen that is displayed
+ * when an application is uninstalled. The result of the application uninstall
+ * is indicated in the result code that gets set to 0 or 1. The application gets launched
+ * by an intent with the intent's class name explicitly set to UninstallAppProgress and expects
+ * the application object of the application to uninstall.
+ */
+public class UninstallAppProgress extends Activity {
+ private static final String TAG = "UninstallAppProgress";
+
+ private static final String FRAGMENT_TAG = "progress_fragment";
+
+ private ApplicationInfo mAppInfo;
+ private boolean mAllUsers;
+ private IBinder mCallback;
+
+ private volatile int mResultCode = -1;
+
+ /**
+ * If initView was called. We delay this call to not have to call it at all if the uninstall is
+ * quick
+ */
+ private boolean mIsViewInitialized;
+
+ /** Amount of time to wait until we show the UI */
+ private static final int QUICK_INSTALL_DELAY_MILLIS = 500;
+
+ private static final int UNINSTALL_COMPLETE = 1;
+ private static final int UNINSTALL_IS_SLOW = 2;
+
+ private Handler mHandler = new MessageHandler(this);
+
+ private static class MessageHandler extends Handler {
+ private final WeakReference mActivity;
+
+ public MessageHandler(UninstallAppProgress activity) {
+ mActivity = new WeakReference<>(activity);
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ UninstallAppProgress activity = mActivity.get();
+ if (activity != null) {
+ activity.handleMessage(msg);
+ }
+ }
+ }
+
+ private void handleMessage(Message msg) {
+ if (isFinishing() || isDestroyed()) {
+ return;
+ }
+
+ switch (msg.what) {
+ case UNINSTALL_IS_SLOW:
+ initView();
+ break;
+ case UNINSTALL_COMPLETE:
+ mHandler.removeMessages(UNINSTALL_IS_SLOW);
+
+ if (msg.arg1 != PackageManager.DELETE_SUCCEEDED) {
+ initView();
+ }
+
+ mResultCode = msg.arg1;
+ final String packageName = (String) msg.obj;
+
+ if (mCallback != null) {
+ final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+ .asInterface(mCallback);
+ try {
+ observer.onPackageDeleted(mAppInfo.packageName, mResultCode,
+ packageName);
+ } catch (RemoteException ignored) {
+ }
+ finish();
+ return;
+ }
+
+ if (getIntent().getBooleanExtra(Intent.EXTRA_RETURN_RESULT, false)) {
+ Intent result = new Intent();
+ result.putExtra(Intent.EXTRA_INSTALL_RESULT, mResultCode);
+ setResult(mResultCode == PackageManager.DELETE_SUCCEEDED
+ ? Activity.RESULT_OK : Activity.RESULT_FIRST_USER,
+ result);
+ finish();
+ return;
+ }
+
+ // Update the status text
+ final String statusText;
+ switch (msg.arg1) {
+ case PackageManager.DELETE_SUCCEEDED:
+ statusText = getString(R.string.uninstall_done);
+ // Show a Toast and finish the activity
+ Context ctx = getBaseContext();
+ Toast.makeText(ctx, statusText, Toast.LENGTH_LONG).show();
+ setResultAndFinish();
+ return;
+ case PackageManager.DELETE_FAILED_DEVICE_POLICY_MANAGER: {
+ UserManager userManager =
+ (UserManager) getSystemService(Context.USER_SERVICE);
+ IDevicePolicyManager dpm = IDevicePolicyManager.Stub.asInterface(
+ ServiceManager.getService(Context.DEVICE_POLICY_SERVICE));
+ // Find out if the package is an active admin for some non-current user.
+ int myUserId = UserHandle.myUserId();
+ UserInfo otherBlockingUser = null;
+ for (UserInfo user : userManager.getUsers()) {
+ // We only catch the case when the user in question is neither the
+ // current user nor its profile.
+ if (isProfileOfOrSame(userManager, myUserId, user.id)) continue;
+
+ try {
+ if (dpm.packageHasActiveAdmins(packageName, user.id)) {
+ otherBlockingUser = user;
+ break;
+ }
+ } catch (RemoteException e) {
+ Log.e(TAG, "Failed to talk to package manager", e);
+ }
+ }
+ if (otherBlockingUser == null) {
+ Log.d(TAG, "Uninstall failed because " + packageName
+ + " is a device admin");
+ getProgressFragment().setDeviceManagerButtonVisible(true);
+ statusText = getString(
+ R.string.uninstall_failed_device_policy_manager);
+ } else {
+ Log.d(TAG, "Uninstall failed because " + packageName
+ + " is a device admin of user " + otherBlockingUser);
+ getProgressFragment().setDeviceManagerButtonVisible(false);
+ statusText = String.format(
+ getString(R.string.uninstall_failed_device_policy_manager_of_user),
+ otherBlockingUser.name);
+ }
+ break;
+ }
+ case PackageManager.DELETE_FAILED_OWNER_BLOCKED: {
+ UserManager userManager =
+ (UserManager) getSystemService(Context.USER_SERVICE);
+ IPackageManager packageManager = IPackageManager.Stub.asInterface(
+ ServiceManager.getService("package"));
+ List users = userManager.getUsers();
+ int blockingUserId = UserHandle.USER_NULL;
+ for (int i = 0; i < users.size(); ++i) {
+ final UserInfo user = users.get(i);
+ try {
+ if (packageManager.getBlockUninstallForUser(packageName,
+ user.id)) {
+ blockingUserId = user.id;
+ break;
+ }
+ } catch (RemoteException e) {
+ // Shouldn't happen.
+ Log.e(TAG, "Failed to talk to package manager", e);
+ }
+ }
+ int myUserId = UserHandle.myUserId();
+ if (isProfileOfOrSame(userManager, myUserId, blockingUserId)) {
+ getProgressFragment().setDeviceManagerButtonVisible(true);
+ } else {
+ getProgressFragment().setDeviceManagerButtonVisible(false);
+ getProgressFragment().setUsersButtonVisible(true);
+ }
+ // TODO: b/25442806
+ if (blockingUserId == UserHandle.USER_SYSTEM) {
+ statusText = getString(R.string.uninstall_blocked_device_owner);
+ } else if (blockingUserId == UserHandle.USER_NULL) {
+ Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+ + msg.arg1 + " no blocking user");
+ statusText = getString(R.string.uninstall_failed);
+ } else {
+ statusText = mAllUsers
+ ? getString(R.string.uninstall_all_blocked_profile_owner) :
+ getString(R.string.uninstall_blocked_profile_owner);
+ }
+ break;
+ }
+ default:
+ Log.d(TAG, "Uninstall failed for " + packageName + " with code "
+ + msg.arg1);
+ statusText = getString(R.string.uninstall_failed);
+ break;
+ }
+ getProgressFragment().showCompletion(statusText);
+ break;
+ default:
+ break;
+ }
+ }
+
+ private boolean isProfileOfOrSame(UserManager userManager, int userId, int profileId) {
+ if (userId == profileId) {
+ return true;
+ }
+ UserInfo parentUser = userManager.getProfileParent(profileId);
+ return parentUser != null && parentUser.id == userId;
+ }
+
+ @Override
+ public void onCreate(Bundle icicle) {
+ super.onCreate(icicle);
+
+ Intent intent = getIntent();
+ mAppInfo = intent.getParcelableExtra(PackageUtil.INTENT_ATTR_APPLICATION_INFO);
+ mCallback = intent.getIBinderExtra(PackageInstaller.EXTRA_CALLBACK);
+
+ // This currently does not support going through a onDestroy->onCreate cycle. Hence if that
+ // happened, just fail the operation for mysterious reasons.
+ if (icicle != null) {
+ mResultCode = PackageManager.DELETE_FAILED_INTERNAL_ERROR;
+
+ if (mCallback != null) {
+ final IPackageDeleteObserver2 observer = IPackageDeleteObserver2.Stub
+ .asInterface(mCallback);
+ try {
+ observer.onPackageDeleted(mAppInfo.packageName, mResultCode, null);
+ } catch (RemoteException ignored) {
+ }
+ finish();
+ } else {
+ setResultAndFinish();
+ }
+
+ return;
+ }
+
+ mAllUsers = intent.getBooleanExtra(Intent.EXTRA_UNINSTALL_ALL_USERS, false);
+ UserHandle user = intent.getParcelableExtra(Intent.EXTRA_USER);
+ if (user == null) {
+ user = android.os.Process.myUserHandle();
+ }
+
+ PackageDeleteObserver observer = new PackageDeleteObserver();
+
+ // Make window transparent until initView is called. In many cases we can avoid showing the
+ // UI at all as the app is uninstalled very quickly. If we show the UI and instantly remove
+ // it, it just looks like a flicker.
+ getWindow().setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
+ getWindow().setStatusBarColor(Color.TRANSPARENT);
+ getWindow().setNavigationBarColor(Color.TRANSPARENT);
+
+ try {
+ getPackageManager().deletePackageAsUser(mAppInfo.packageName, observer,
+ mAllUsers ? PackageManager.DELETE_ALL_USERS : 0, user.getIdentifier());
+ } catch (IllegalArgumentException e) {
+ // Couldn't find the package, no need to call uninstall.
+ Log.w(TAG, "Could not find package, not deleting " + mAppInfo.packageName, e);
+ }
+
+ mHandler.sendMessageDelayed(mHandler.obtainMessage(UNINSTALL_IS_SLOW),
+ QUICK_INSTALL_DELAY_MILLIS);
+ }
+
+ public ApplicationInfo getAppInfo() {
+ return mAppInfo;
+ }
+
+ private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+ public void packageDeleted(String packageName, int returnCode) {
+ Message msg = mHandler.obtainMessage(UNINSTALL_COMPLETE);
+ msg.arg1 = returnCode;
+ msg.obj = packageName;
+ mHandler.sendMessage(msg);
+ }
+ }
+
+ public void setResultAndFinish() {
+ setResult(mResultCode);
+ finish();
+ }
+
+ private void initView() {
+ if (mIsViewInitialized) {
+ return;
+ }
+ mIsViewInitialized = true;
+
+ // We set the window background to translucent in constructor, revert this
+ TypedValue attribute = new TypedValue();
+ getTheme().resolveAttribute(android.R.attr.windowBackground, attribute, true);
+ if (attribute.type >= TypedValue.TYPE_FIRST_COLOR_INT &&
+ attribute.type <= TypedValue.TYPE_LAST_COLOR_INT) {
+ getWindow().setBackgroundDrawable(new ColorDrawable(attribute.data));
+ } else {
+ getWindow().setBackgroundDrawable(getResources().getDrawable(attribute.resourceId,
+ getTheme()));
+ }
+
+ getTheme().resolveAttribute(android.R.attr.navigationBarColor, attribute, true);
+ getWindow().setNavigationBarColor(attribute.data);
+
+ getTheme().resolveAttribute(android.R.attr.statusBarColor, attribute, true);
+ getWindow().setStatusBarColor(attribute.data);
+
+ boolean isUpdate = ((mAppInfo.flags & ApplicationInfo.FLAG_UPDATED_SYSTEM_APP) != 0);
+ setTitle(isUpdate ? R.string.uninstall_update_title : R.string.uninstall_application_title);
+
+ getFragmentManager().beginTransaction()
+ .add(android.R.id.content, new UninstallAppProgressFragment(), FRAGMENT_TAG)
+ .commitNowAllowingStateLoss();
+ }
+
+ @Override
+ public boolean dispatchKeyEvent(KeyEvent ev) {
+ if (ev.getKeyCode() == KeyEvent.KEYCODE_BACK) {
+ if (mResultCode == -1) {
+ // Ignore back key when installation is in progress
+ return true;
+ } else {
+ // If installation is done, just set the result code
+ setResult(mResultCode);
+ }
+ }
+ return super.dispatchKeyEvent(ev);
+ }
+
+ private ProgressFragment getProgressFragment() {
+ return (ProgressFragment) getFragmentManager().findFragmentByTag(FRAGMENT_TAG);
+ }
+
+ public interface ProgressFragment {
+ void setUsersButtonVisible(boolean visible);
+ void setDeviceManagerButtonVisible(boolean visible);
+ void showCompletion(CharSequence statusText);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
new file mode 100644
index 0000000000000..7402a7dae7f3e
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/television/UninstallAppProgressFragment.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.television;
+
+import android.app.Fragment;
+import android.content.Intent;
+import android.os.Bundle;
+import android.provider.Settings;
+import androidx.annotation.Nullable;
+import android.util.Log;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.Button;
+import android.widget.TextView;
+
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+public class UninstallAppProgressFragment extends Fragment implements View.OnClickListener,
+ UninstallAppProgress.ProgressFragment {
+ private static final String TAG = "UninstallAppProgressF"; // full class name is too long
+
+ private Button mOkButton;
+ private Button mDeviceManagerButton;
+ private Button mUsersButton;
+
+ @Override
+ public View onCreateView(LayoutInflater inflater, @Nullable ViewGroup container,
+ Bundle savedInstanceState) {
+ final View root = inflater.inflate(R.layout.uninstall_progress, container, false);
+ // Initialize views
+ View snippetView = root.findViewById(R.id.app_snippet);
+ PackageUtil.initSnippetForInstalledApp(getContext(),
+ ((UninstallAppProgress)getActivity()).getAppInfo(), snippetView);
+ mDeviceManagerButton = (Button) root.findViewById(R.id.device_manager_button);
+ mUsersButton = (Button) root.findViewById(R.id.users_button);
+ mDeviceManagerButton.setVisibility(View.GONE);
+ mDeviceManagerButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent();
+ intent.setClassName("com.android.settings",
+ "com.android.settings.Settings$DeviceAdminSettingsActivity");
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ getActivity().finish();
+ }
+ });
+ mUsersButton.setVisibility(View.GONE);
+ mUsersButton.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ Intent intent = new Intent(Settings.ACTION_USER_SETTINGS);
+ intent.setFlags(Intent.FLAG_ACTIVITY_NO_HISTORY | Intent.FLAG_ACTIVITY_NEW_TASK);
+ startActivity(intent);
+ getActivity().finish();
+ }
+ });
+ // Hide button till progress is being displayed
+ mOkButton = (Button) root.findViewById(R.id.ok_button);
+ mOkButton.setOnClickListener(this);
+
+ return root;
+ }
+
+ public void onClick(View v) {
+ final UninstallAppProgress activity = (UninstallAppProgress) getActivity();
+ if(v == mOkButton && activity != null) {
+ Log.i(TAG, "Finished uninstalling pkg: " +
+ activity.getAppInfo().packageName);
+ activity.setResultAndFinish();
+ }
+ }
+
+ @Override
+ public void setUsersButtonVisible(boolean visible) {
+ mUsersButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void setDeviceManagerButtonVisible(boolean visible) {
+ mDeviceManagerButton.setVisibility(visible ? View.VISIBLE : View.GONE);
+ }
+
+ @Override
+ public void showCompletion(CharSequence statusText) {
+ final View root = getView();
+ root.findViewById(R.id.progress_view).setVisibility(View.GONE);
+ root.findViewById(R.id.status_view).setVisibility(View.VISIBLE);
+ ((TextView) root.findViewById(R.id.status_text)).setText(statusText);
+ root.findViewById(R.id.ok_panel).setVisibility(View.VISIBLE);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
new file mode 100644
index 0000000000000..53a460dc18ca8
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallTask.java
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Context;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.os.Looper;
+import android.os.ParcelFileDescriptor;
+import android.text.TextUtils;
+import android.util.Log;
+
+import java.io.Closeable;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+/**
+ * Task that installs an APK. This must not be called on the main thread.
+ * This code is based off the Finsky/Wearsky implementation
+ */
+public class InstallTask {
+ private static final String TAG = "InstallTask";
+
+ private static final int DEFAULT_BUFFER_SIZE = 8192;
+
+ private final Context mContext;
+ private String mPackageName;
+ private ParcelFileDescriptor mParcelFileDescriptor;
+ private PackageInstallerImpl.InstallListener mCallback;
+ private PackageInstaller.Session mSession;
+ private IntentSender mCommitCallback;
+
+ private Exception mException = null;
+ private int mErrorCode = 0;
+ private String mErrorDesc = null;
+
+ public InstallTask(Context context, String packageName,
+ ParcelFileDescriptor parcelFileDescriptor,
+ PackageInstallerImpl.InstallListener callback, PackageInstaller.Session session,
+ IntentSender commitCallback) {
+ mContext = context;
+ mPackageName = packageName;
+ mParcelFileDescriptor = parcelFileDescriptor;
+ mCallback = callback;
+ mSession = session;
+ mCommitCallback = commitCallback;
+ }
+
+ public boolean isError() {
+ return mErrorCode != InstallerConstants.STATUS_SUCCESS || !TextUtils.isEmpty(mErrorDesc);
+ }
+
+ public void execute() {
+ if (Looper.myLooper() == Looper.getMainLooper()) {
+ throw new IllegalStateException("This method cannot be called from the UI thread.");
+ }
+
+ OutputStream sessionStream = null;
+ try {
+ sessionStream = mSession.openWrite(mPackageName, 0, -1);
+
+ // 2b: Stream the asset to the installer. Note:
+ // Note: writeToOutputStreamFromAsset() always safely closes the input stream
+ writeToOutputStreamFromAsset(sessionStream);
+ mSession.fsync(sessionStream);
+ } catch (Exception e) {
+ mException = e;
+ mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM;
+ mErrorDesc = "Could not write to stream";
+ } finally {
+ if (sessionStream != null) {
+ // 2c: close output stream
+ try {
+ sessionStream.close();
+ } catch (Exception e) {
+ // Ignore otherwise
+ if (mException == null) {
+ mException = e;
+ mErrorCode = InstallerConstants.ERROR_INSTALL_CLOSE_STREAM;
+ mErrorDesc = "Could not close session stream";
+ }
+ }
+ }
+ }
+
+ if (mErrorCode != InstallerConstants.STATUS_SUCCESS) {
+ // An error occurred, we're done
+ Log.e(TAG, "Exception while installing " + mPackageName + ": " + mErrorCode + ", "
+ + mErrorDesc + ", " + mException);
+ mSession.close();
+ mCallback.installFailed(mErrorCode, "[" + mPackageName + "]" + mErrorDesc);
+ } else {
+ // 3. Commit the session (this actually installs it.) Session map
+ // will be cleaned up in the callback.
+ mCallback.installBeginning();
+ mSession.commit(mCommitCallback);
+ mSession.close();
+ }
+ }
+
+ /**
+ * {@code PackageInstaller} works with streams. Get the {@code FileDescriptor}
+ * corresponding to the {@code Asset} and then write the contents into an
+ * {@code OutputStream} that is passed in.
+ *
+ * The {@code FileDescriptor} is closed but the {@code OutputStream} is not closed.
+ */
+ private boolean writeToOutputStreamFromAsset(OutputStream outputStream) {
+ if (outputStream == null) {
+ mErrorCode = InstallerConstants.ERROR_INSTALL_COPY_STREAM_EXCEPTION;
+ mErrorDesc = "Got a null OutputStream.";
+ return false;
+ }
+
+ if (mParcelFileDescriptor == null || mParcelFileDescriptor.getFileDescriptor() == null) {
+ mErrorCode = InstallerConstants.ERROR_COULD_NOT_GET_FD;
+ mErrorDesc = "Could not get FD";
+ return false;
+ }
+
+ InputStream inputStream = null;
+ try {
+ byte[] inputBuf = new byte[DEFAULT_BUFFER_SIZE];
+ int bytesRead;
+ inputStream = new ParcelFileDescriptor.AutoCloseInputStream(mParcelFileDescriptor);
+
+ while ((bytesRead = inputStream.read(inputBuf)) > -1) {
+ if (bytesRead > 0) {
+ outputStream.write(inputBuf, 0, bytesRead);
+ }
+ }
+
+ outputStream.flush();
+ } catch (IOException e) {
+ mErrorCode = InstallerConstants.ERROR_INSTALL_APK_COPY_FAILURE;
+ mErrorDesc = "Reading from Asset FD or writing to temp file failed: " + e;
+ return false;
+ } finally {
+ safeClose(inputStream);
+ }
+
+ return true;
+ }
+
+ /**
+ * Quietly close a closeable resource (e.g. a stream or file). The input may already
+ * be closed and it may even be null.
+ */
+ public static void safeClose(Closeable resource) {
+ if (resource != null) {
+ try {
+ resource.close();
+ } catch (IOException ioe) {
+ // Catch and discard the error
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
new file mode 100644
index 0000000000000..3daf3d831d971
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/InstallerConstants.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+/**
+ * Constants for Installation / Uninstallation requests.
+ * Using the same values as Finsky/Wearsky code for consistency in user analytics of failures
+ */
+public class InstallerConstants {
+ /** Request succeeded */
+ public static final int STATUS_SUCCESS = 0;
+
+ /**
+ * The new PackageInstaller also returns a small set of less granular error codes, which
+ * we'll remap to the range -500 and below to keep away from existing installer codes
+ * (which run from -1 to -110).
+ */
+ public final static int ERROR_PACKAGEINSTALLER_BASE = -500;
+
+ public static final int ERROR_COULD_NOT_GET_FD = -603;
+ /** This node is not targeted by this request. */
+
+ /** The install did not complete because could not create PackageInstaller session */
+ public final static int ERROR_INSTALL_CREATE_SESSION = -612;
+ /** The install did not complete because could not open PackageInstaller session */
+ public final static int ERROR_INSTALL_OPEN_SESSION = -613;
+ /** The install did not complete because could not open PackageInstaller output stream */
+ public final static int ERROR_INSTALL_OPEN_STREAM = -614;
+ /** The install did not complete because of an exception while streaming bytes */
+ public final static int ERROR_INSTALL_COPY_STREAM_EXCEPTION = -615;
+ /** The install did not complete because of an unexpected exception from PackageInstaller */
+ public final static int ERROR_INSTALL_SESSION_EXCEPTION = -616;
+ /** The install did not complete because of an unexpected userActionRequired callback */
+ public final static int ERROR_INSTALL_USER_ACTION_REQUIRED = -617;
+ /** The install did not complete because of an unexpected broadcast (missing fields) */
+ public final static int ERROR_INSTALL_MALFORMED_BROADCAST = -618;
+ /** The install did not complete because of an error while copying from downloaded file */
+ public final static int ERROR_INSTALL_APK_COPY_FAILURE = -619;
+ /** The install did not complete because of an error while copying to the PackageInstaller
+ * output stream */
+ public final static int ERROR_INSTALL_COPY_STREAM = -620;
+ /** The install did not complete because of an error while closing the PackageInstaller
+ * output stream */
+ public final static int ERROR_INSTALL_CLOSE_STREAM = -621;
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
new file mode 100644
index 0000000000000..bdc22cf0e2764
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerFactory.java
@@ -0,0 +1,36 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.content.Context;
+
+/**
+ * Factory that creates a Package Installer.
+ */
+public class PackageInstallerFactory {
+ private static PackageInstallerImpl sPackageInstaller;
+
+ /**
+ * Return the PackageInstaller shared object. {@code init} should have already been called.
+ */
+ public synchronized static PackageInstallerImpl getPackageInstaller(Context context) {
+ if (sPackageInstaller == null) {
+ sPackageInstaller = new PackageInstallerImpl(context);
+ }
+ return sPackageInstaller;
+ }
+}
\ No newline at end of file
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
new file mode 100644
index 0000000000000..bf4b03c56b797
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/PackageInstallerImpl.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.packageinstaller.wear;
+
+import android.annotation.TargetApi;
+import android.app.PendingIntent;
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.IntentFilter;
+import android.content.IntentSender;
+import android.content.pm.PackageInstaller;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.List;
+import java.util.Map;
+
+/**
+ * Implementation of package manager installation using modern PackageInstaller api.
+ *
+ * Heavily copied from Wearsky/Finsky implementation
+ */
+@TargetApi(Build.VERSION_CODES.LOLLIPOP)
+public class PackageInstallerImpl {
+ private static final String TAG = "PackageInstallerImpl";
+
+ /** Intent actions used for broadcasts from PackageInstaller back to the local receiver */
+ private static final String ACTION_INSTALL_COMMIT =
+ "com.android.vending.INTENT_PACKAGE_INSTALL_COMMIT";
+
+ private final Context mContext;
+ private final PackageInstaller mPackageInstaller;
+ private final Map mSessionInfoMap;
+ private final Map mOpenSessionMap;
+
+ public PackageInstallerImpl(Context context) {
+ mContext = context.getApplicationContext();
+ mPackageInstaller = mContext.getPackageManager().getPackageInstaller();
+
+ // Capture a map of known sessions
+ // This list will be pruned a bit later (stale sessions will be canceled)
+ mSessionInfoMap = new HashMap();
+ List mySessions = mPackageInstaller.getMySessions();
+ for (int i = 0; i < mySessions.size(); i++) {
+ PackageInstaller.SessionInfo sessionInfo = mySessions.get(i);
+ String packageName = sessionInfo.getAppPackageName();
+ PackageInstaller.SessionInfo oldInfo = mSessionInfoMap.put(packageName, sessionInfo);
+
+ // Checking for old info is strictly for logging purposes
+ if (oldInfo != null) {
+ Log.w(TAG, "Multiple sessions for " + packageName + " found. Removing " + oldInfo
+ .getSessionId() + " & keeping " + mySessions.get(i).getSessionId());
+ }
+ }
+ mOpenSessionMap = new HashMap();
+ }
+
+ /**
+ * This callback will be made after an installation attempt succeeds or fails.
+ */
+ public interface InstallListener {
+ /**
+ * This callback signals that preflight checks have succeeded and installation
+ * is beginning.
+ */
+ void installBeginning();
+
+ /**
+ * This callback signals that installation has completed.
+ */
+ void installSucceeded();
+
+ /**
+ * This callback signals that installation has failed.
+ */
+ void installFailed(int errorCode, String errorDesc);
+ }
+
+ /**
+ * This is a placeholder implementation that bundles an entire "session" into a single
+ * call. This will be replaced by more granular versions that allow longer session lifetimes,
+ * download progress tracking, etc.
+ *
+ * This must not be called on main thread.
+ */
+ public void install(final String packageName, ParcelFileDescriptor parcelFileDescriptor,
+ final InstallListener callback) {
+ // 0. Generic try/catch block because I am not really sure what exceptions (other than
+ // IOException) might be thrown by PackageInstaller and I want to handle them
+ // at least slightly gracefully.
+ try {
+ // 1. Create or recover a session, and open it
+ // Try recovery first
+ PackageInstaller.Session session = null;
+ PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
+ if (sessionInfo != null) {
+ // See if it's openable, or already held open
+ session = getSession(packageName);
+ }
+ // If open failed, or there was no session, create a new one and open it.
+ // If we cannot create or open here, the failure is terminal.
+ if (session == null) {
+ try {
+ innerCreateSession(packageName);
+ } catch (IOException ioe) {
+ Log.e(TAG, "Can't create session for " + packageName + ": " + ioe.getMessage());
+ callback.installFailed(InstallerConstants.ERROR_INSTALL_CREATE_SESSION,
+ "Could not create session");
+ mSessionInfoMap.remove(packageName);
+ return;
+ }
+ sessionInfo = mSessionInfoMap.get(packageName);
+ try {
+ session = mPackageInstaller.openSession(sessionInfo.getSessionId());
+ mOpenSessionMap.put(packageName, session);
+ } catch (SecurityException se) {
+ Log.e(TAG, "Can't open session for " + packageName + ": " + se.getMessage());
+ callback.installFailed(InstallerConstants.ERROR_INSTALL_OPEN_SESSION,
+ "Can't open session");
+ mSessionInfoMap.remove(packageName);
+ return;
+ }
+ }
+
+ // 2. Launch task to handle file operations.
+ InstallTask task = new InstallTask( mContext, packageName, parcelFileDescriptor,
+ callback, session,
+ getCommitCallback(packageName, sessionInfo.getSessionId(), callback));
+ task.execute();
+ if (task.isError()) {
+ cancelSession(sessionInfo.getSessionId(), packageName);
+ }
+ } catch (Exception e) {
+ Log.e(TAG, "Unexpected exception while installing: " + packageName + ": "
+ + e.getMessage());
+ callback.installFailed(InstallerConstants.ERROR_INSTALL_SESSION_EXCEPTION,
+ "Unexpected exception while installing " + packageName);
+ }
+ }
+
+ /**
+ * Retrieve an existing session. Will open if needed, but does not attempt to create.
+ */
+ private PackageInstaller.Session getSession(String packageName) {
+ // Check for already-open session
+ PackageInstaller.Session session = mOpenSessionMap.get(packageName);
+ if (session != null) {
+ try {
+ // Probe the session to ensure that it's still open. This may or may not
+ // throw (if non-open), but it may serve as a canary for stale sessions.
+ session.getNames();
+ return session;
+ } catch (IOException ioe) {
+ Log.e(TAG, "Stale open session for " + packageName + ": " + ioe.getMessage());
+ mOpenSessionMap.remove(packageName);
+ } catch (SecurityException se) {
+ Log.e(TAG, "Stale open session for " + packageName + ": " + se.getMessage());
+ mOpenSessionMap.remove(packageName);
+ }
+ }
+ // Check to see if this is a known session
+ PackageInstaller.SessionInfo sessionInfo = mSessionInfoMap.get(packageName);
+ if (sessionInfo == null) {
+ return null;
+ }
+ // Try to open it. If we fail here, assume that the SessionInfo was stale.
+ try {
+ session = mPackageInstaller.openSession(sessionInfo.getSessionId());
+ } catch (SecurityException se) {
+ Log.w(TAG, "SessionInfo was stale for " + packageName + " - deleting info");
+ mSessionInfoMap.remove(packageName);
+ return null;
+ } catch (IOException ioe) {
+ Log.w(TAG, "IOException opening old session for " + ioe.getMessage()
+ + " - deleting info");
+ mSessionInfoMap.remove(packageName);
+ return null;
+ }
+ mOpenSessionMap.put(packageName, session);
+ return session;
+ }
+
+ /** This version throws an IOException when the session cannot be created */
+ private void innerCreateSession(String packageName) throws IOException {
+ if (mSessionInfoMap.containsKey(packageName)) {
+ Log.w(TAG, "Creating session for " + packageName + " when one already exists");
+ return;
+ }
+ PackageInstaller.SessionParams params = new PackageInstaller.SessionParams(
+ PackageInstaller.SessionParams.MODE_FULL_INSTALL);
+ params.setAppPackageName(packageName);
+
+ // IOException may be thrown at this point
+ int sessionId = mPackageInstaller.createSession(params);
+ PackageInstaller.SessionInfo sessionInfo = mPackageInstaller.getSessionInfo(sessionId);
+ mSessionInfoMap.put(packageName, sessionInfo);
+ }
+
+ /**
+ * Cancel a session based on its sessionId. Package name is for logging only.
+ */
+ private void cancelSession(int sessionId, String packageName) {
+ // Close if currently held open
+ closeSession(packageName);
+ // Remove local record
+ mSessionInfoMap.remove(packageName);
+ try {
+ mPackageInstaller.abandonSession(sessionId);
+ } catch (SecurityException se) {
+ // The session no longer exists, so we can exit quietly.
+ return;
+ }
+ }
+
+ /**
+ * Close a session if it happens to be held open.
+ */
+ private void closeSession(String packageName) {
+ PackageInstaller.Session session = mOpenSessionMap.remove(packageName);
+ if (session != null) {
+ // Unfortunately close() is not idempotent. Try our best to make this safe.
+ try {
+ session.close();
+ } catch (Exception e) {
+ Log.w(TAG, "Unexpected error closing session for " + packageName + ": "
+ + e.getMessage());
+ }
+ }
+ }
+
+ /**
+ * Creates a commit callback for the package install that's underway. This will be called
+ * some time after calling session.commit() (above).
+ */
+ private IntentSender getCommitCallback(final String packageName, final int sessionId,
+ final InstallListener callback) {
+ // Create a single-use broadcast receiver
+ BroadcastReceiver broadcastReceiver = new BroadcastReceiver() {
+ @Override
+ public void onReceive(Context context, Intent intent) {
+ mContext.unregisterReceiver(this);
+ handleCommitCallback(intent, packageName, sessionId, callback);
+ }
+ };
+ // Create a matching intent-filter and register the receiver
+ String action = ACTION_INSTALL_COMMIT + "." + packageName;
+ IntentFilter intentFilter = new IntentFilter();
+ intentFilter.addAction(action);
+ mContext.registerReceiver(broadcastReceiver, intentFilter);
+
+ // Create a matching PendingIntent and use it to generate the IntentSender
+ Intent broadcastIntent = new Intent(action);
+ PendingIntent pendingIntent = PendingIntent.getBroadcast(mContext, packageName.hashCode(),
+ broadcastIntent, PendingIntent.FLAG_ONE_SHOT | PendingIntent.FLAG_UPDATE_CURRENT);
+ return pendingIntent.getIntentSender();
+ }
+
+ /**
+ * Examine the extras to determine information about the package update/install, decode
+ * the result, and call the appropriate callback.
+ *
+ * @param intent The intent, which the PackageInstaller will have added Extras to
+ * @param packageName The package name we created the receiver for
+ * @param sessionId The session Id we created the receiver for
+ * @param callback The callback to report success/failure to
+ */
+ private void handleCommitCallback(Intent intent, String packageName, int sessionId,
+ InstallListener callback) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Installation of " + packageName + " finished with extras "
+ + intent.getExtras());
+ }
+ String statusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE);
+ int status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, Integer.MIN_VALUE);
+ if (status == PackageInstaller.STATUS_SUCCESS) {
+ cancelSession(sessionId, packageName);
+ callback.installSucceeded();
+ } else if (status == -1 /*PackageInstaller.STATUS_USER_ACTION_REQUIRED*/) {
+ // TODO - use the constant when the correct/final name is in the SDK
+ // TODO This is unexpected, so we are treating as failure for now
+ cancelSession(sessionId, packageName);
+ callback.installFailed(InstallerConstants.ERROR_INSTALL_USER_ACTION_REQUIRED,
+ "Unexpected: user action required");
+ } else {
+ cancelSession(sessionId, packageName);
+ int errorCode = getPackageManagerErrorCode(status);
+ Log.e(TAG, "Error " + errorCode + " while installing " + packageName + ": "
+ + statusMessage);
+ callback.installFailed(errorCode, null);
+ }
+ }
+
+ private int getPackageManagerErrorCode(int status) {
+ // This is a hack: because PackageInstaller now reports error codes
+ // with small positive values, we need to remap them into a space
+ // that is more compatible with the existing package manager error codes.
+ // See https://sites.google.com/a/google.com/universal-store/documentation
+ // /android-client/download-error-codes
+ int errorCode;
+ if (status == Integer.MIN_VALUE) {
+ errorCode = InstallerConstants.ERROR_INSTALL_MALFORMED_BROADCAST;
+ } else {
+ errorCode = InstallerConstants.ERROR_PACKAGEINSTALLER_BASE - status;
+ }
+ return errorCode;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
new file mode 100644
index 0000000000000..2c289b2a6f94f
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageArgs.java
@@ -0,0 +1,100 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.wear;
+
+import android.content.Intent;
+import android.net.Uri;
+import android.os.Bundle;
+
+/**
+ * Installation Util that contains a list of parameters that are needed for
+ * installing/uninstalling.
+ */
+public class WearPackageArgs {
+ private static final String KEY_PACKAGE_NAME =
+ "com.google.android.clockwork.EXTRA_PACKAGE_NAME";
+ private static final String KEY_ASSET_URI =
+ "com.google.android.clockwork.EXTRA_ASSET_URI";
+ private static final String KEY_START_ID =
+ "com.google.android.clockwork.EXTRA_START_ID";
+ private static final String KEY_PERM_URI =
+ "com.google.android.clockwork.EXTRA_PERM_URI";
+ private static final String KEY_CHECK_PERMS =
+ "com.google.android.clockwork.EXTRA_CHECK_PERMS";
+ private static final String KEY_SKIP_IF_SAME_VERSION =
+ "com.google.android.clockwork.EXTRA_SKIP_IF_SAME_VERSION";
+ private static final String KEY_COMPRESSION_ALG =
+ "com.google.android.clockwork.EXTRA_KEY_COMPRESSION_ALG";
+ private static final String KEY_COMPANION_SDK_VERSION =
+ "com.google.android.clockwork.EXTRA_KEY_COMPANION_SDK_VERSION";
+ private static final String KEY_COMPANION_DEVICE_VERSION =
+ "com.google.android.clockwork.EXTRA_KEY_COMPANION_DEVICE_VERSION";
+ private static final String KEY_SHOULD_CHECK_GMS_DEPENDENCY =
+ "com.google.android.clockwork.EXTRA_KEY_SHOULD_CHECK_GMS_DEPENDENCY";
+ private static final String KEY_SKIP_IF_LOWER_VERSION =
+ "com.google.android.clockwork.EXTRA_SKIP_IF_LOWER_VERSION";
+
+ public static String getPackageName(Bundle b) {
+ return b.getString(KEY_PACKAGE_NAME);
+ }
+
+ public static Bundle setPackageName(Bundle b, String packageName) {
+ b.putString(KEY_PACKAGE_NAME, packageName);
+ return b;
+ }
+
+ public static Uri getAssetUri(Bundle b) {
+ return b.getParcelable(KEY_ASSET_URI);
+ }
+
+ public static Uri getPermUri(Bundle b) {
+ return b.getParcelable(KEY_PERM_URI);
+ }
+
+ public static boolean checkPerms(Bundle b) {
+ return b.getBoolean(KEY_CHECK_PERMS);
+ }
+
+ public static boolean skipIfSameVersion(Bundle b) {
+ return b.getBoolean(KEY_SKIP_IF_SAME_VERSION);
+ }
+
+ public static int getCompanionSdkVersion(Bundle b) {
+ return b.getInt(KEY_COMPANION_SDK_VERSION);
+ }
+
+ public static int getCompanionDeviceVersion(Bundle b) {
+ return b.getInt(KEY_COMPANION_DEVICE_VERSION);
+ }
+
+ public static String getCompressionAlg(Bundle b) {
+ return b.getString(KEY_COMPRESSION_ALG);
+ }
+
+ public static int getStartId(Bundle b) {
+ return b.getInt(KEY_START_ID);
+ }
+
+ public static boolean skipIfLowerVersion(Bundle b) {
+ return b.getBoolean(KEY_SKIP_IF_LOWER_VERSION, false);
+ }
+
+ public static Bundle setStartId(Bundle b, int startId) {
+ b.putInt(KEY_START_ID, startId);
+ return b;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
new file mode 100644
index 0000000000000..02b9d298db0e5
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageIconProvider.java
@@ -0,0 +1,202 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.wear;
+
+import android.annotation.TargetApi;
+import android.app.ActivityManager;
+import android.content.ContentProvider;
+import android.content.ContentValues;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.ParcelFileDescriptor;
+import android.util.Log;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.List;
+
+import static android.content.pm.PackageManager.PERMISSION_GRANTED;
+
+public class WearPackageIconProvider extends ContentProvider {
+ private static final String TAG = "WearPackageIconProvider";
+ public static final String AUTHORITY = "com.google.android.packageinstaller.wear.provider";
+
+ private static final String REQUIRED_PERMISSION =
+ "com.google.android.permission.INSTALL_WEARABLE_PACKAGES";
+
+ /** MIME types. */
+ public static final String ICON_TYPE = "vnd.android.cursor.item/cw_package_icon";
+
+ @Override
+ public boolean onCreate() {
+ return true;
+ }
+
+ @Override
+ public Cursor query(Uri uri, String[] projection, String selection, String[] selectionArgs,
+ String sortOrder) {
+ throw new UnsupportedOperationException("Query is not supported.");
+ }
+
+ @Override
+ public String getType(Uri uri) {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI passed in is null.");
+ }
+
+ if (AUTHORITY.equals(uri.getEncodedAuthority())) {
+ return ICON_TYPE;
+ }
+ return null;
+ }
+
+ @Override
+ public Uri insert(Uri uri, ContentValues values) {
+ throw new UnsupportedOperationException("Insert is not supported.");
+ }
+
+ @Override
+ public int delete(Uri uri, String selection, String[] selectionArgs) {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI passed in is null.");
+ }
+
+ enforcePermissions(uri);
+
+ if (ICON_TYPE.equals(getType(uri))) {
+ final File file = WearPackageUtil.getIconFile(
+ this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
+ if (file != null) {
+ file.delete();
+ }
+ }
+
+ return 0;
+ }
+
+ @Override
+ public int update(Uri uri, ContentValues values, String selection, String[] selectionArgs) {
+ throw new UnsupportedOperationException("Update is not supported.");
+ }
+
+ @Override
+ public ParcelFileDescriptor openFile(
+ Uri uri, @SuppressWarnings("unused") String mode) throws FileNotFoundException {
+ if (uri == null) {
+ throw new IllegalArgumentException("URI passed in is null.");
+ }
+
+ enforcePermissions(uri);
+
+ if (ICON_TYPE.equals(getType(uri))) {
+ final File file = WearPackageUtil.getIconFile(
+ this.getContext().getApplicationContext(), getPackageNameFromUri(uri));
+ if (file != null) {
+ return ParcelFileDescriptor.open(file, ParcelFileDescriptor.MODE_READ_ONLY);
+ }
+ }
+ return null;
+ }
+
+ public static Uri getUriForPackage(final String packageName) {
+ return Uri.parse("content://" + AUTHORITY + "/icons/" + packageName + ".icon");
+ }
+
+ private String getPackageNameFromUri(Uri uri) {
+ if (uri == null) {
+ return null;
+ }
+ List pathSegments = uri.getPathSegments();
+ String packageName = pathSegments.get(pathSegments.size() - 1);
+
+ if (packageName.endsWith(".icon")) {
+ packageName = packageName.substring(0, packageName.lastIndexOf("."));
+ }
+ return packageName;
+ }
+
+ /**
+ * Make sure the calling app is either a system app or the same app or has the right permission.
+ * @throws SecurityException if the caller has insufficient permissions.
+ */
+ @TargetApi(Build.VERSION_CODES.BASE_1_1)
+ private void enforcePermissions(Uri uri) {
+ // Redo some of the permission check in {@link ContentProvider}. Just add an extra check to
+ // allow System process to access this provider.
+ Context context = getContext();
+ final int pid = Binder.getCallingPid();
+ final int uid = Binder.getCallingUid();
+ final int myUid = android.os.Process.myUid();
+
+ if (uid == myUid || isSystemApp(context, pid)) {
+ return;
+ }
+
+ if (context.checkPermission(REQUIRED_PERMISSION, pid, uid) == PERMISSION_GRANTED) {
+ return;
+ }
+
+ // last chance, check against any uri grants
+ if (context.checkUriPermission(uri, pid, uid, Intent.FLAG_GRANT_READ_URI_PERMISSION)
+ == PERMISSION_GRANTED) {
+ return;
+ }
+
+ throw new SecurityException("Permission Denial: reading "
+ + getClass().getName() + " uri " + uri + " from pid=" + pid
+ + ", uid=" + uid);
+ }
+
+ /**
+ * From the pid of the calling process, figure out whether this is a system app or not. We do
+ * this by checking the application information corresponding to the pid and then checking if
+ * FLAG_SYSTEM is set.
+ */
+ @TargetApi(Build.VERSION_CODES.CUPCAKE)
+ private boolean isSystemApp(Context context, int pid) {
+ // Get the Activity Manager Object
+ ActivityManager aManager =
+ (ActivityManager) context.getSystemService(Context.ACTIVITY_SERVICE);
+ // Get the list of running Applications
+ List rapInfoList =
+ aManager.getRunningAppProcesses();
+ for (ActivityManager.RunningAppProcessInfo rapInfo : rapInfoList) {
+ if (rapInfo.pid == pid) {
+ try {
+ PackageInfo pkgInfo = context.getPackageManager().getPackageInfo(
+ rapInfo.pkgList[0], 0);
+ if (pkgInfo != null && pkgInfo.applicationInfo != null &&
+ (pkgInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) != 0) {
+ Log.d(TAG, pid + " is a system app.");
+ return true;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ Log.e(TAG, "Could not find package information.", e);
+ return false;
+ }
+ }
+ }
+ return false;
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
new file mode 100644
index 0000000000000..e5f7613ab52b0
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageInstallerService.java
@@ -0,0 +1,589 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.wear;
+
+import android.app.Notification;
+import android.app.NotificationChannel;
+import android.app.NotificationManager;
+import android.app.Service;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.FeatureInfo;
+import android.content.pm.IPackageDeleteObserver;
+import android.content.pm.PackageInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.PackageParser;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.Build;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.HandlerThread;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.ParcelFileDescriptor;
+import android.os.PowerManager;
+import android.os.Process;
+import android.util.ArrayMap;
+import android.util.Log;
+import android.util.Pair;
+
+import com.android.packageinstaller.DeviceUtils;
+import com.android.packageinstaller.PackageUtil;
+import com.android.packageinstaller.R;
+
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+/**
+ * Service that will install/uninstall packages. It will check for permissions and features as well.
+ *
+ * -----------
+ *
+ * Debugging information:
+ *
+ * Install Action example:
+ * adb shell am startservice -a com.android.packageinstaller.wear.INSTALL_PACKAGE \
+ * -d package://com.google.android.gms \
+ * --eu com.google.android.clockwork.EXTRA_ASSET_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/wearable/com.google.android.gms/apk \
+ * --es android.intent.extra.INSTALLER_PACKAGE_NAME com.google.android.gms \
+ * --ez com.google.android.clockwork.EXTRA_CHECK_PERMS false \
+ * --eu com.google.android.clockwork.EXTRA_PERM_URI content://com.google.android.clockwork.home.provider/host/com.google.android.wearable.app/permissions \
+ * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ *
+ * Uninstall Action example:
+ * adb shell am startservice -a com.android.packageinstaller.wear.UNINSTALL_PACKAGE \
+ * -d package://com.google.android.gms \
+ * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ *
+ * Retry GMS:
+ * adb shell am startservice -a com.android.packageinstaller.wear.RETRY_GMS \
+ * com.android.packageinstaller/com.android.packageinstaller.wear.WearPackageInstallerService
+ */
+public class WearPackageInstallerService extends Service {
+ private static final String TAG = "WearPkgInstallerService";
+
+ private static final String WEAR_APPS_CHANNEL = "wear_app_install_uninstall";
+
+ private final int START_INSTALL = 1;
+ private final int START_UNINSTALL = 2;
+
+ private int mInstallNotificationId = 1;
+ private final Map mNotifIdMap = new ArrayMap<>();
+
+ private final class ServiceHandler extends Handler {
+ public ServiceHandler(Looper looper) {
+ super(looper);
+ }
+
+ public void handleMessage(Message msg) {
+ switch (msg.what) {
+ case START_INSTALL:
+ installPackage(msg.getData());
+ break;
+ case START_UNINSTALL:
+ uninstallPackage(msg.getData());
+ break;
+ }
+ }
+ }
+ private ServiceHandler mServiceHandler;
+ private NotificationChannel mNotificationChannel;
+ private static volatile PowerManager.WakeLock lockStatic = null;
+
+ @Override
+ public IBinder onBind(Intent intent) {
+ return null;
+ }
+
+ @Override
+ public void onCreate() {
+ super.onCreate();
+ HandlerThread thread = new HandlerThread("PackageInstallerThread",
+ Process.THREAD_PRIORITY_BACKGROUND);
+ thread.start();
+
+ mServiceHandler = new ServiceHandler(thread.getLooper());
+ }
+
+ @Override
+ public int onStartCommand(Intent intent, int flags, int startId) {
+ if (!DeviceUtils.isWear(this)) {
+ Log.w(TAG, "Not running on wearable.");
+ finishServiceEarly(startId);
+ return START_NOT_STICKY;
+ }
+
+ if (intent == null) {
+ Log.w(TAG, "Got null intent.");
+ finishServiceEarly(startId);
+ return START_NOT_STICKY;
+ }
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Got install/uninstall request " + intent);
+ }
+
+ Uri packageUri = intent.getData();
+ if (packageUri == null) {
+ Log.e(TAG, "No package URI in intent");
+ finishServiceEarly(startId);
+ return START_NOT_STICKY;
+ }
+
+ final String packageName = WearPackageUtil.getSanitizedPackageName(packageUri);
+ if (packageName == null) {
+ Log.e(TAG, "Invalid package name in URI (expected package:): " + packageUri);
+ finishServiceEarly(startId);
+ return START_NOT_STICKY;
+ }
+
+ PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+ if (!lock.isHeld()) {
+ lock.acquire();
+ }
+
+ Bundle intentBundle = intent.getExtras();
+ if (intentBundle == null) {
+ intentBundle = new Bundle();
+ }
+ WearPackageArgs.setStartId(intentBundle, startId);
+ WearPackageArgs.setPackageName(intentBundle, packageName);
+ Message msg;
+ String notifTitle;
+ if (Intent.ACTION_INSTALL_PACKAGE.equals(intent.getAction())) {
+ msg = mServiceHandler.obtainMessage(START_INSTALL);
+ notifTitle = getString(R.string.installing);
+ } else if (Intent.ACTION_UNINSTALL_PACKAGE.equals(intent.getAction())) {
+ msg = mServiceHandler.obtainMessage(START_UNINSTALL);
+ notifTitle = getString(R.string.uninstalling);
+ } else {
+ Log.e(TAG, "Unknown action : " + intent.getAction());
+ finishServiceEarly(startId);
+ return START_NOT_STICKY;
+ }
+ Pair notifPair = buildNotification(packageName, notifTitle);
+ startForeground(notifPair.first, notifPair.second);
+ msg.setData(intentBundle);
+ mServiceHandler.sendMessage(msg);
+ return START_NOT_STICKY;
+ }
+
+ private void installPackage(Bundle argsBundle) {
+ int startId = WearPackageArgs.getStartId(argsBundle);
+ final String packageName = WearPackageArgs.getPackageName(argsBundle);
+ final Uri assetUri = WearPackageArgs.getAssetUri(argsBundle);
+ final Uri permUri = WearPackageArgs.getPermUri(argsBundle);
+ boolean checkPerms = WearPackageArgs.checkPerms(argsBundle);
+ boolean skipIfSameVersion = WearPackageArgs.skipIfSameVersion(argsBundle);
+ int companionSdkVersion = WearPackageArgs.getCompanionSdkVersion(argsBundle);
+ int companionDeviceVersion = WearPackageArgs.getCompanionDeviceVersion(argsBundle);
+ String compressionAlg = WearPackageArgs.getCompressionAlg(argsBundle);
+ boolean skipIfLowerVersion = WearPackageArgs.skipIfLowerVersion(argsBundle);
+
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Installing package: " + packageName + ", assetUri: " + assetUri +
+ ",permUri: " + permUri + ", startId: " + startId + ", checkPerms: " +
+ checkPerms + ", skipIfSameVersion: " + skipIfSameVersion +
+ ", compressionAlg: " + compressionAlg + ", companionSdkVersion: " +
+ companionSdkVersion + ", companionDeviceVersion: " + companionDeviceVersion +
+ ", skipIfLowerVersion: " + skipIfLowerVersion);
+ }
+ final PackageManager pm = getPackageManager();
+ File tempFile = null;
+ int installFlags = 0;
+ PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+ boolean messageSent = false;
+ try {
+ PackageInfo existingPkgInfo = null;
+ try {
+ existingPkgInfo = pm.getPackageInfo(packageName,
+ PackageManager.MATCH_ANY_USER | PackageManager.GET_PERMISSIONS);
+ if (existingPkgInfo != null) {
+ installFlags |= PackageManager.INSTALL_REPLACE_EXISTING;
+ }
+ } catch (PackageManager.NameNotFoundException e) {
+ // Ignore this exception. We could not find the package, will treat as a new
+ // installation.
+ }
+ if ((installFlags & PackageManager.INSTALL_REPLACE_EXISTING) != 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, "Replacing package:" + packageName);
+ }
+ }
+ // TODO(28021618): This was left as a temp file due to the fact that this code is being
+ // deprecated and that we need the bare minimum to continue working moving forward
+ // If this code is used as reference, this permission logic might want to be
+ // reworked to use a stream instead of a file so that we don't need to write a
+ // file at all. Note that there might be some trickiness with opening a stream
+ // for multiple users.
+ ParcelFileDescriptor parcelFd = getContentResolver()
+ .openFileDescriptor(assetUri, "r");
+ tempFile = WearPackageUtil.getFileFromFd(WearPackageInstallerService.this,
+ parcelFd, packageName, compressionAlg);
+ if (tempFile == null) {
+ Log.e(TAG, "Could not create a temp file from FD for " + packageName);
+ return;
+ }
+ PackageParser.Package pkg = PackageUtil.getPackageInfo(this, tempFile);
+ if (pkg == null) {
+ Log.e(TAG, "Could not parse apk information for " + packageName);
+ return;
+ }
+
+ if (!pkg.packageName.equals(packageName)) {
+ Log.e(TAG, "Wearable Package Name has to match what is provided for " +
+ packageName);
+ return;
+ }
+
+ pkg.applicationInfo.sourceDir = tempFile.getPath();
+ pkg.applicationInfo.publicSourceDir = tempFile.getPath();
+ getLabelAndUpdateNotification(packageName,
+ getString(R.string.installing_app, pkg.applicationInfo.loadLabel(pm)));
+
+ List wearablePerms = pkg.requestedPermissions;
+
+ // Log if the installed pkg has a higher version number.
+ if (existingPkgInfo != null) {
+ if (existingPkgInfo.getLongVersionCode() == pkg.getLongVersionCode()) {
+ if (skipIfSameVersion) {
+ Log.w(TAG, "Version number (" + pkg.getLongVersionCode() +
+ ") of new app is equal to existing app for " + packageName +
+ "; not installing due to versionCheck");
+ return;
+ } else {
+ Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ ") is equal to existing app for " + packageName);
+ }
+ } else if (existingPkgInfo.getLongVersionCode() > pkg.getLongVersionCode()) {
+ if (skipIfLowerVersion) {
+ // Starting in Feldspar, we are not going to allow downgrades of any app.
+ Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ ") is lower than existing app ( "
+ + existingPkgInfo.getLongVersionCode() +
+ ") for " + packageName + "; not installing due to versionCheck");
+ return;
+ } else {
+ Log.w(TAG, "Version number of new app (" + pkg.getLongVersionCode() +
+ ") is lower than existing app ( "
+ + existingPkgInfo.getLongVersionCode() + ") for " + packageName);
+ }
+ }
+
+ // Following the Android Phone model, we should only check for permissions for any
+ // newly defined perms.
+ if (existingPkgInfo.requestedPermissions != null) {
+ for (int i = 0; i < existingPkgInfo.requestedPermissions.length; ++i) {
+ // If the permission is granted, then we will not ask to request it again.
+ if ((existingPkgInfo.requestedPermissionsFlags[i] &
+ PackageInfo.REQUESTED_PERMISSION_GRANTED) != 0) {
+ if (Log.isLoggable(TAG, Log.DEBUG)) {
+ Log.d(TAG, existingPkgInfo.requestedPermissions[i] +
+ " is already granted for " + packageName);
+ }
+ wearablePerms.remove(existingPkgInfo.requestedPermissions[i]);
+ }
+ }
+ }
+ }
+
+ // Check that the wearable has all the features.
+ boolean hasAllFeatures = true;
+ if (pkg.reqFeatures != null) {
+ for (FeatureInfo feature : pkg.reqFeatures) {
+ if (feature.name != null && !pm.hasSystemFeature(feature.name) &&
+ (feature.flags & FeatureInfo.FLAG_REQUIRED) != 0) {
+ Log.e(TAG, "Wearable does not have required feature: " + feature +
+ " for " + packageName);
+ hasAllFeatures = false;
+ }
+ }
+ }
+
+ if (!hasAllFeatures) {
+ return;
+ }
+
+ // Check permissions on both the new wearable package and also on the already installed
+ // wearable package.
+ // If the app is targeting API level 23, we will also start a service in ClockworkHome
+ // which will ultimately prompt the user to accept/reject permissions.
+ if (checkPerms && !checkPermissions(pkg, companionSdkVersion, companionDeviceVersion,
+ permUri, wearablePerms, tempFile)) {
+ Log.w(TAG, "Wearable does not have enough permissions.");
+ return;
+ }
+
+ // Finally install the package.
+ ParcelFileDescriptor fd = getContentResolver().openFileDescriptor(assetUri, "r");
+ PackageInstallerFactory.getPackageInstaller(this).install(packageName, fd,
+ new PackageInstallListener(this, lock, startId, packageName));
+
+ messageSent = true;
+ Log.i(TAG, "Sent installation request for " + packageName);
+ } catch (FileNotFoundException e) {
+ Log.e(TAG, "Could not find the file with URI " + assetUri, e);
+ } finally {
+ if (!messageSent) {
+ // Some error happened. If the message has been sent, we can wait for the observer
+ // which will finish the service.
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ finishService(lock, startId);
+ }
+ }
+ }
+
+ // TODO: This was left using the old PackageManager API due to the fact that this code is being
+ // deprecated and that we need the bare minimum to continue working moving forward
+ // If this code is used as reference, this logic should be reworked to use the new
+ // PackageInstaller APIs similar to how installPackage was reworked
+ private void uninstallPackage(Bundle argsBundle) {
+ int startId = WearPackageArgs.getStartId(argsBundle);
+ final String packageName = WearPackageArgs.getPackageName(argsBundle);
+
+ PowerManager.WakeLock lock = getLock(this.getApplicationContext());
+ final PackageManager pm = getPackageManager();
+ try {
+ PackageInfo pkgInfo = pm.getPackageInfo(packageName, 0);
+ getLabelAndUpdateNotification(packageName,
+ getString(R.string.uninstalling_app, pkgInfo.applicationInfo.loadLabel(pm)));
+
+ // Found package, send uninstall request.
+ pm.deletePackage(packageName, new PackageDeleteObserver(lock, startId),
+ PackageManager.DELETE_ALL_USERS);
+
+ Log.i(TAG, "Sent delete request for " + packageName);
+ } catch (IllegalArgumentException | PackageManager.NameNotFoundException e) {
+ // Couldn't find the package, no need to call uninstall.
+ Log.w(TAG, "Could not find package, not deleting " + packageName, e);
+ finishService(lock, startId);
+ }
+ }
+
+ private boolean checkPermissions(PackageParser.Package pkg, int companionSdkVersion,
+ int companionDeviceVersion, Uri permUri, List wearablePermissions,
+ File apkFile) {
+ // Assumption: We are running on Android O.
+ // If the Phone App is targeting M, all permissions may not have been granted to the phone
+ // app. If the Wear App is then not targeting M, there may be permissions that are not
+ // granted on the Phone app (by the user) right now and we cannot just grant it for the Wear
+ // app.
+ if (pkg.applicationInfo.targetSdkVersion >= Build.VERSION_CODES.M) {
+ // Install the app if Wear App is ready for the new perms model.
+ return true;
+ }
+
+ if (!doesWearHaveUngrantedPerms(pkg.packageName, permUri, wearablePermissions)) {
+ // All permissions requested by the watch are already granted on the phone, no need
+ // to do anything.
+ return true;
+ }
+
+ // Log an error if Wear is targeting < 23 and phone is targeting >= 23.
+ if (companionSdkVersion == 0 || companionSdkVersion >= Build.VERSION_CODES.M) {
+ Log.e(TAG, "MNC: Wear app's targetSdkVersion should be at least 23, if "
+ + "phone app is targeting at least 23, will continue.");
+ }
+
+ return false;
+ }
+
+ /**
+ * Given a {@string packageName} corresponding to a phone app, query the provider for all the
+ * perms that are granted.
+ *
+ * @return true if the Wear App has any perms that have not been granted yet on the phone side.
+ * @return true if there is any error cases.
+ */
+ private boolean doesWearHaveUngrantedPerms(String packageName, Uri permUri,
+ List wearablePermissions) {
+ if (permUri == null) {
+ Log.e(TAG, "Permission URI is null");
+ // Pretend there is an ungranted permission to avoid installing for error cases.
+ return true;
+ }
+ Cursor permCursor = getContentResolver().query(permUri, null, null, null, null);
+ if (permCursor == null) {
+ Log.e(TAG, "Could not get the cursor for the permissions");
+ // Pretend there is an ungranted permission to avoid installing for error cases.
+ return true;
+ }
+
+ Set grantedPerms = new HashSet<>();
+ Set ungrantedPerms = new HashSet<>();
+ while(permCursor.moveToNext()) {
+ // Make sure that the MatrixCursor returned by the ContentProvider has 2 columns and
+ // verify their types.
+ if (permCursor.getColumnCount() == 2
+ && Cursor.FIELD_TYPE_STRING == permCursor.getType(0)
+ && Cursor.FIELD_TYPE_INTEGER == permCursor.getType(1)) {
+ String perm = permCursor.getString(0);
+ Integer granted = permCursor.getInt(1);
+ if (granted == 1) {
+ grantedPerms.add(perm);
+ } else {
+ ungrantedPerms.add(perm);
+ }
+ }
+ }
+ permCursor.close();
+
+ boolean hasUngrantedPerm = false;
+ for (String wearablePerm : wearablePermissions) {
+ if (!grantedPerms.contains(wearablePerm)) {
+ hasUngrantedPerm = true;
+ if (!ungrantedPerms.contains(wearablePerm)) {
+ // This is an error condition. This means that the wearable has permissions that
+ // are not even declared in its host app. This is a developer error.
+ Log.e(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm
+ + "\" that is not defined in the host application's manifest.");
+ } else {
+ Log.w(TAG, "Wearable " + packageName + " has a permission \"" + wearablePerm +
+ "\" that is not granted in the host application.");
+ }
+ }
+ }
+ return hasUngrantedPerm;
+ }
+
+ /** Finishes the service after fulfilling obligation to call startForeground. */
+ private void finishServiceEarly(int startId) {
+ Pair notifPair = buildNotification(
+ getApplicationContext().getPackageName(), "");
+ startForeground(notifPair.first, notifPair.second);
+ finishService(null, startId);
+ }
+
+ private void finishService(PowerManager.WakeLock lock, int startId) {
+ if (lock != null && lock.isHeld()) {
+ lock.release();
+ }
+ stopSelf(startId);
+ }
+
+ private synchronized PowerManager.WakeLock getLock(Context context) {
+ if (lockStatic == null) {
+ PowerManager mgr =
+ (PowerManager) context.getSystemService(Context.POWER_SERVICE);
+ lockStatic = mgr.newWakeLock(
+ PowerManager.PARTIAL_WAKE_LOCK, context.getClass().getSimpleName());
+ lockStatic.setReferenceCounted(true);
+ }
+ return lockStatic;
+ }
+
+ private class PackageInstallListener implements PackageInstallerImpl.InstallListener {
+ private Context mContext;
+ private PowerManager.WakeLock mWakeLock;
+ private int mStartId;
+ private String mApplicationPackageName;
+ private PackageInstallListener(Context context, PowerManager.WakeLock wakeLock,
+ int startId, String applicationPackageName) {
+ mContext = context;
+ mWakeLock = wakeLock;
+ mStartId = startId;
+ mApplicationPackageName = applicationPackageName;
+ }
+
+ @Override
+ public void installBeginning() {
+ Log.i(TAG, "Package " + mApplicationPackageName + " is being installed.");
+ }
+
+ @Override
+ public void installSucceeded() {
+ try {
+ Log.i(TAG, "Package " + mApplicationPackageName + " was installed.");
+
+ // Delete tempFile from the file system.
+ File tempFile = WearPackageUtil.getTemporaryFile(mContext, mApplicationPackageName);
+ if (tempFile != null) {
+ tempFile.delete();
+ }
+ } finally {
+ finishService(mWakeLock, mStartId);
+ }
+ }
+
+ @Override
+ public void installFailed(int errorCode, String errorDesc) {
+ Log.e(TAG, "Package install failed " + mApplicationPackageName
+ + ", errorCode " + errorCode);
+ finishService(mWakeLock, mStartId);
+ }
+ }
+
+ private class PackageDeleteObserver extends IPackageDeleteObserver.Stub {
+ private PowerManager.WakeLock mWakeLock;
+ private int mStartId;
+
+ private PackageDeleteObserver(PowerManager.WakeLock wakeLock, int startId) {
+ mWakeLock = wakeLock;
+ mStartId = startId;
+ }
+
+ public void packageDeleted(String packageName, int returnCode) {
+ try {
+ if (returnCode >= 0) {
+ Log.i(TAG, "Package " + packageName + " was uninstalled.");
+ } else {
+ Log.e(TAG, "Package uninstall failed " + packageName + ", returnCode " +
+ returnCode);
+ }
+ } finally {
+ finishService(mWakeLock, mStartId);
+ }
+ }
+ }
+
+ private synchronized Pair buildNotification(final String packageName,
+ final String title) {
+ int notifId;
+ if (mNotifIdMap.containsKey(packageName)) {
+ notifId = mNotifIdMap.get(packageName);
+ } else {
+ notifId = mInstallNotificationId++;
+ mNotifIdMap.put(packageName, notifId);
+ }
+
+ if (mNotificationChannel == null) {
+ mNotificationChannel = new NotificationChannel(WEAR_APPS_CHANNEL,
+ getString(R.string.wear_app_channel), NotificationManager.IMPORTANCE_MIN);
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ notificationManager.createNotificationChannel(mNotificationChannel);
+ }
+ return new Pair<>(notifId, new Notification.Builder(this, WEAR_APPS_CHANNEL)
+ .setSmallIcon(R.drawable.ic_file_download)
+ .setContentTitle(title)
+ .build());
+ }
+
+ private void getLabelAndUpdateNotification(String packageName, String title) {
+ // Update notification since we have a label now.
+ NotificationManager notificationManager = getSystemService(NotificationManager.class);
+ Pair notifPair = buildNotification(packageName, title);
+ notificationManager.notify(notifPair.first, notifPair.second);
+ }
+}
diff --git a/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
new file mode 100644
index 0000000000000..bc740ab15a904
--- /dev/null
+++ b/packages/PackageInstaller/src/com/android/packageinstaller/wear/WearPackageUtil.java
@@ -0,0 +1,133 @@
+/*
+ * Copyright (C) 2015 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.packageinstaller.wear;
+
+import android.content.Context;
+import android.net.Uri;
+import android.os.ParcelFileDescriptor;
+import android.system.ErrnoException;
+import android.system.Os;
+import android.text.TextUtils;
+import android.util.Log;
+
+import org.tukaani.xz.LZMAInputStream;
+import org.tukaani.xz.XZInputStream;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+public class WearPackageUtil {
+ private static final String TAG = "WearablePkgInstaller";
+
+ private static final String COMPRESSION_LZMA = "lzma";
+ private static final String COMPRESSION_XZ = "xz";
+
+ public static File getTemporaryFile(Context context, String packageName) {
+ try {
+ File newFileDir = new File(context.getFilesDir(), "tmp");
+ newFileDir.mkdirs();
+ Os.chmod(newFileDir.getAbsolutePath(), 0771);
+ File newFile = new File(newFileDir, packageName + ".apk");
+ return newFile;
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed to open.", e);
+ return null;
+ }
+ }
+
+ public static File getIconFile(final Context context, final String packageName) {
+ try {
+ File newFileDir = new File(context.getFilesDir(), "images/icons");
+ newFileDir.mkdirs();
+ Os.chmod(newFileDir.getAbsolutePath(), 0771);
+ return new File(newFileDir, packageName + ".icon");
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Failed to open.", e);
+ return null;
+ }
+ }
+
+ /**
+ * In order to make sure that the Wearable Asset Manager has a reasonable apk that can be used
+ * by the PackageManager, we will parse it before sending it to the PackageManager.
+ * Unfortunately, PackageParser needs a file to parse. So, we have to temporarily convert the fd
+ * to a File.
+ *
+ * @param context
+ * @param fd FileDescriptor to convert to File
+ * @param packageName Name of package, will define the name of the file
+ * @param compressionAlg Can be null. For ALT mode the APK will be compressed. We will
+ * decompress it here
+ */
+ public static File getFileFromFd(Context context, ParcelFileDescriptor fd,
+ String packageName, String compressionAlg) {
+ File newFile = getTemporaryFile(context, packageName);
+ if (fd == null || fd.getFileDescriptor() == null) {
+ return null;
+ }
+ InputStream fr = new ParcelFileDescriptor.AutoCloseInputStream(fd);
+ try {
+ if (TextUtils.equals(compressionAlg, COMPRESSION_XZ)) {
+ fr = new XZInputStream(fr);
+ } else if (TextUtils.equals(compressionAlg, COMPRESSION_LZMA)) {
+ fr = new LZMAInputStream(fr);
+ }
+ } catch (IOException e) {
+ Log.e(TAG, "Compression was set to " + compressionAlg + ", but could not decode ", e);
+ return null;
+ }
+
+ int nRead;
+ byte[] data = new byte[1024];
+ try {
+ final FileOutputStream fo = new FileOutputStream(newFile);
+ while ((nRead = fr.read(data, 0, data.length)) != -1) {
+ fo.write(data, 0, nRead);
+ }
+ fo.flush();
+ fo.close();
+ Os.chmod(newFile.getAbsolutePath(), 0644);
+ return newFile;
+ } catch (IOException e) {
+ Log.e(TAG, "Reading from Asset FD or writing to temp file failed ", e);
+ return null;
+ } catch (ErrnoException e) {
+ Log.e(TAG, "Could not set permissions on file ", e);
+ return null;
+ } finally {
+ try {
+ fr.close();
+ } catch (IOException e) {
+ Log.e(TAG, "Failed to close the file from FD ", e);
+ }
+ }
+ }
+
+ /**
+ * @return com.google.com from expected formats like
+ * Uri: package:com.google.com, package:/com.google.com, package://com.google.com
+ */
+ public static String getSanitizedPackageName(Uri packageUri) {
+ String packageName = packageUri.getEncodedSchemeSpecificPart();
+ if (packageName != null) {
+ return packageName.replaceAll("^/+", "");
+ }
+ return packageName;
+ }
+}