summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--res/values/strings.xml2
-rw-r--r--src/com/android/documentsui/base/Shared.java12
-rw-r--r--src/com/android/documentsui/dirlist/DirectoryFragment.java5
-rw-r--r--src/com/android/documentsui/dirlist/DocumentsAdapter.java1
-rw-r--r--tests/common/com/android/documentsui/dirlist/TestEnvironment.java5
-rw-r--r--tests/unit/com/android/documentsui/picker/ApplicationNameTest.kt91
6 files changed, 102 insertions, 14 deletions
diff --git a/res/values/strings.xml b/res/values/strings.xml
index c3f11bab4..4cd85344b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -575,7 +575,7 @@
<!-- Confrim dialog title show on open document tree flow. [CHAR_LIMIT=80] -->
<string name="open_tree_dialog_title">Allow <xliff:g id="appName" example="Drive">%1$s</xliff:g> to access files in <xliff:g id="directory" example="DCIM">%2$s</xliff:g>?</string>
<!-- Confrim dialog message show on open document tree flow.-->
- <string name="open_tree_dialog_message">This will let <xliff:g id="appName" example="Drive">%1$s</xliff:g> access current and future content stored in <xliff:g id="directory" example="DCIM">%2$s</xliff:g>.</string>
+ <string name="open_tree_dialog_message">This will let "<xliff:g id="appName" example="Drive">%1$s</xliff:g>" access current and future content stored in <xliff:g id="directory" example="DCIM">%2$s</xliff:g>.</string>
<!-- Header message title show on open document tree flow when directory is blocked. [CHAR_LIMIT=48] -->
<string name="directory_blocked_header_title">Can\u2019t use this folder</string>
<!-- Header message subtitle show on open document tree flow when directory is blocked. [CHAR_LIMIT=90]-->
diff --git a/src/com/android/documentsui/base/Shared.java b/src/com/android/documentsui/base/Shared.java
index ac089999f..bb8a39393 100644
--- a/src/com/android/documentsui/base/Shared.java
+++ b/src/com/android/documentsui/base/Shared.java
@@ -16,6 +16,9 @@
package com.android.documentsui.base;
+import static android.text.TextUtils.SAFE_STRING_FLAG_SINGLE_LINE;
+import static android.text.TextUtils.SAFE_STRING_FLAG_TRIM;
+
import static com.android.documentsui.base.SharedMinimal.TAG;
import static com.android.documentsui.ChangeIds.RESTRICT_STORAGE_ACCESS_FRAMEWORK;
@@ -265,7 +268,7 @@ public final class Shared {
* @return the calling app name or general anonymous name if not found
*/
@NonNull
- public static String getCallingAppName(Activity activity) {
+ public static CharSequence getCallingAppName(Activity activity) {
final String anonymous = activity.getString(R.string.anonymous_application);
final String packageName = getCallingPackageName(activity);
if (TextUtils.isEmpty(packageName)) {
@@ -281,7 +284,12 @@ public final class Shared {
}
CharSequence result = pm.getApplicationLabel(ai);
- return TextUtils.isEmpty(result) ? anonymous : result.toString();
+ if (TextUtils.isEmpty(result)) {
+ return anonymous;
+ }
+
+ return TextUtils.makeSafeForPresentation(
+ result.toString(), 500, 0, SAFE_STRING_FLAG_TRIM | SAFE_STRING_FLAG_SINGLE_LINE);
}
/**
diff --git a/src/com/android/documentsui/dirlist/DirectoryFragment.java b/src/com/android/documentsui/dirlist/DirectoryFragment.java
index 2911d04e9..ae728f616 100644
--- a/src/com/android/documentsui/dirlist/DirectoryFragment.java
+++ b/src/com/android/documentsui/dirlist/DirectoryFragment.java
@@ -1634,10 +1634,5 @@ public class DirectoryFragment extends Fragment implements SwipeRefreshLayout.On
public ActionHandler getActionHandler() {
return mActions;
}
-
- @Override
- public String getCallingAppName() {
- return Shared.getCallingAppName(mActivity);
- }
}
}
diff --git a/src/com/android/documentsui/dirlist/DocumentsAdapter.java b/src/com/android/documentsui/dirlist/DocumentsAdapter.java
index 41ce73c8c..b32c15335 100644
--- a/src/com/android/documentsui/dirlist/DocumentsAdapter.java
+++ b/src/com/android/documentsui/dirlist/DocumentsAdapter.java
@@ -90,7 +90,6 @@ public abstract class DocumentsAdapter extends RecyclerView.Adapter<DocumentHold
boolean isInSearchMode();
boolean isSelected(String id);
Model getModel();
- String getCallingAppName();
boolean isDocumentEnabled(String mimeType, int flags);
void initDocumentHolder(DocumentHolder holder);
void onBindDocumentHolder(DocumentHolder holder, Cursor cursor);
diff --git a/tests/common/com/android/documentsui/dirlist/TestEnvironment.java b/tests/common/com/android/documentsui/dirlist/TestEnvironment.java
index 2e91e0733..cf50f0780 100644
--- a/tests/common/com/android/documentsui/dirlist/TestEnvironment.java
+++ b/tests/common/com/android/documentsui/dirlist/TestEnvironment.java
@@ -88,9 +88,4 @@ public final class TestEnvironment implements DocumentsAdapter.Environment {
@Override
public void onBindDocumentHolder(DocumentHolder holder, Cursor cursor) {
}
-
- @Override
- public String getCallingAppName() {
- return "unknown";
- }
}
diff --git a/tests/unit/com/android/documentsui/picker/ApplicationNameTest.kt b/tests/unit/com/android/documentsui/picker/ApplicationNameTest.kt
new file mode 100644
index 000000000..7ee3b9aa6
--- /dev/null
+++ b/tests/unit/com/android/documentsui/picker/ApplicationNameTest.kt
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2025 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.documentsui.picker
+
+import android.app.Activity
+import android.content.pm.ApplicationInfo
+import android.content.pm.PackageManager
+import android.content.res.Resources
+import com.android.documentsui.R
+import com.android.documentsui.base.Shared
+import org.junit.Assert.assertEquals
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.junit.runners.Parameterized
+import org.mockito.Mock
+import org.mockito.Mockito.eq
+import org.mockito.Mockito.`when` as whenever
+import org.mockito.MockitoAnnotations
+
+/**
+ * When apps are requesting access to a folder using `ACTION_OPEN_DOCUMENT_TREE` the application
+ * label is shown in the UI. Let's ensure that the label is appropriately sanitised.
+ */
+@RunWith(Parameterized::class)
+class ApplicationNameTest {
+ @Mock
+ private lateinit var mockActivity: Activity
+
+ @Mock
+ private lateinit var resources: Resources
+
+ @Mock
+ private lateinit var pm: PackageManager
+
+ companion object {
+ const val PACKAGE_NAME = "com.package.test"
+ const val ANONYMOUS_PACKAGE = "anonymous"
+
+ @Parameterized.Parameters(name = "{index}")
+ @JvmStatic
+ fun parameters() =
+ listOf(
+ // Normal labels return their values.
+ Pair("label", "label"),
+
+ // Newlines are stripped.
+ Pair("label\nlabel", "label label"),
+
+ // Labels are trimmed to 500 characters.
+ Pair("a".repeat(600), "a".repeat(500)),
+
+ // An empty label returns the anonymous package name.
+ Pair("", ANONYMOUS_PACKAGE)
+ )
+ }
+
+ @Parameterized.Parameter(0)
+ lateinit var testData: Pair<String, String>
+
+ @Before
+ fun setUp() {
+ MockitoAnnotations.openMocks(this)
+ whenever(mockActivity.resources).thenReturn(resources)
+ whenever(mockActivity.packageManager).thenReturn(pm)
+ whenever(resources.getString(R.string.anonymous_application)).thenReturn(ANONYMOUS_PACKAGE)
+ whenever<String>(mockActivity.callingPackage).thenReturn(PACKAGE_NAME)
+ }
+
+ @Test
+ fun testNameIsSanitized() {
+ val info = ApplicationInfo()
+ whenever(pm.getApplicationInfo(PACKAGE_NAME, 0)).thenReturn(info)
+
+ whenever(pm.getApplicationLabel(eq(info))).thenReturn(testData.first)
+ assertEquals(Shared.getCallingAppName(mockActivity), testData.second)
+ }
+}