summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/TimePickerDialog.java3
-rw-r--r--core/java/android/os/FileUtils.java80
-rw-r--r--core/tests/coretests/src/android/os/FileUtilsTest.java45
-rw-r--r--docs/html/about/dashboards/index.jd52
-rw-r--r--docs/html/design/media/wear/ContextualExample.006.pngbin185830 -> 156910 bytes
-rw-r--r--docs/html/design/media/wear/ContextualExample.006_2x.pngbin657190 -> 544968 bytes
-rw-r--r--docs/html/design/wear/context.jd2
-rwxr-xr-xlibs/hwui/OpenGLRenderer.cpp8
-rw-r--r--packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java111
-rw-r--r--packages/ExternalStorageProvider/tests/Android.mk16
-rw-r--r--packages/ExternalStorageProvider/tests/AndroidManifest.xml13
-rw-r--r--packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java90
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java14
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java7
-rw-r--r--services/core/java/com/android/server/ConnectivityService.java4
-rw-r--r--services/core/java/com/android/server/pm/PackageManagerService.java18
-rw-r--r--services/core/java/com/android/server/pm/UserManagerService.java14
17 files changed, 384 insertions, 93 deletions
diff --git a/core/java/android/app/TimePickerDialog.java b/core/java/android/app/TimePickerDialog.java
index 6454367..3a2c21b 100644
--- a/core/java/android/app/TimePickerDialog.java
+++ b/core/java/android/app/TimePickerDialog.java
@@ -134,6 +134,9 @@ public class TimePickerDialog extends AlertDialog implements OnClickListener,
mTimePicker.getCurrentMinute());
}
break;
+ case BUTTON_NEGATIVE:
+ cancel();
+ break;
}
}
diff --git a/core/java/android/os/FileUtils.java b/core/java/android/os/FileUtils.java
index fe47f5b..0a724a1 100644
--- a/core/java/android/os/FileUtils.java
+++ b/core/java/android/os/FileUtils.java
@@ -17,9 +17,8 @@
package android.os;
import android.system.ErrnoException;
-import android.text.TextUtils;
import android.system.Os;
-import android.system.OsConstants;
+import android.text.TextUtils;
import android.util.Log;
import android.util.Slog;
@@ -403,20 +402,89 @@ public class FileUtils {
return success;
}
+ private static boolean isValidExtFilenameChar(char c) {
+ switch (c) {
+ case '\0':
+ case '/':
+ return false;
+ default:
+ return true;
+ }
+ }
+
/**
- * Assert that given filename is valid on ext4.
+ * Check if given filename is valid for an ext4 filesystem.
*/
public static boolean isValidExtFilename(String name) {
+ return (name != null) && name.equals(buildValidExtFilename(name));
+ }
+
+ /**
+ * Mutate the given filename to make it valid for an ext4 filesystem,
+ * replacing any invalid characters with "_".
+ */
+ public static String buildValidExtFilename(String name) {
if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
- return false;
+ return "(invalid)";
}
+ final StringBuilder res = new StringBuilder(name.length());
for (int i = 0; i < name.length(); i++) {
final char c = name.charAt(i);
- if (c == '\0' || c == '/') {
+ if (isValidExtFilenameChar(c)) {
+ res.append(c);
+ } else {
+ res.append('_');
+ }
+ }
+ return res.toString();
+ }
+
+ private static boolean isValidFatFilenameChar(char c) {
+ if ((0x00 <= c && c <= 0x1f)) {
+ return false;
+ }
+ switch (c) {
+ case '"':
+ case '*':
+ case '/':
+ case ':':
+ case '<':
+ case '>':
+ case '?':
+ case '\\':
+ case '|':
+ case 0x7F:
return false;
+ default:
+ return true;
+ }
+ }
+
+ /**
+ * Check if given filename is valid for a FAT filesystem.
+ */
+ public static boolean isValidFatFilename(String name) {
+ return (name != null) && name.equals(buildValidFatFilename(name));
+ }
+
+ /**
+ * Mutate the given filename to make it valid for a FAT filesystem,
+ * replacing any invalid characters with "_".
+ */
+ public static String buildValidFatFilename(String name) {
+ if (TextUtils.isEmpty(name) || ".".equals(name) || "..".equals(name)) {
+ return "(invalid)";
+ }
+ final StringBuilder res = new StringBuilder(name.length());
+ for (int i = 0; i < name.length(); i++) {
+ final char c = name.charAt(i);
+ if (isValidFatFilenameChar(c)) {
+ res.append(c);
+ } else {
+ res.append('_');
}
}
- return true;
+ return res.toString();
}
public static String rewriteAfterRename(File beforeDir, File afterDir, String path) {
diff --git a/core/tests/coretests/src/android/os/FileUtilsTest.java b/core/tests/coretests/src/android/os/FileUtilsTest.java
index 93e68eb..5c9e813 100644
--- a/core/tests/coretests/src/android/os/FileUtilsTest.java
+++ b/core/tests/coretests/src/android/os/FileUtilsTest.java
@@ -180,6 +180,51 @@ public class FileUtilsTest extends AndroidTestCase {
assertDirContents("file1", "file2");
}
+ public void testValidExtFilename() throws Exception {
+ assertTrue(FileUtils.isValidExtFilename("a"));
+ assertTrue(FileUtils.isValidExtFilename("foo.bar"));
+ assertTrue(FileUtils.isValidExtFilename("foo bar.baz"));
+ assertTrue(FileUtils.isValidExtFilename("foo.bar.baz"));
+ assertTrue(FileUtils.isValidExtFilename(".bar"));
+ assertTrue(FileUtils.isValidExtFilename("foo~!@#$%^&*()_[]{}+bar"));
+
+ assertFalse(FileUtils.isValidExtFilename(null));
+ assertFalse(FileUtils.isValidExtFilename("."));
+ assertFalse(FileUtils.isValidExtFilename("../foo"));
+ assertFalse(FileUtils.isValidExtFilename("/foo"));
+
+ assertEquals(".._foo", FileUtils.buildValidExtFilename("../foo"));
+ assertEquals("_foo", FileUtils.buildValidExtFilename("/foo"));
+ assertEquals("foo_bar", FileUtils.buildValidExtFilename("foo\0bar"));
+ assertEquals(".foo", FileUtils.buildValidExtFilename(".foo"));
+ assertEquals("foo.bar", FileUtils.buildValidExtFilename("foo.bar"));
+ }
+
+ public void testValidFatFilename() throws Exception {
+ assertTrue(FileUtils.isValidFatFilename("a"));
+ assertTrue(FileUtils.isValidFatFilename("foo bar.baz"));
+ assertTrue(FileUtils.isValidFatFilename("foo.bar.baz"));
+ assertTrue(FileUtils.isValidFatFilename(".bar"));
+ assertTrue(FileUtils.isValidFatFilename("foo.bar"));
+ assertTrue(FileUtils.isValidFatFilename("foo bar"));
+ assertTrue(FileUtils.isValidFatFilename("foo+bar"));
+ assertTrue(FileUtils.isValidFatFilename("foo,bar"));
+
+ assertFalse(FileUtils.isValidFatFilename("foo*bar"));
+ assertFalse(FileUtils.isValidFatFilename("foo?bar"));
+ assertFalse(FileUtils.isValidFatFilename("foo<bar"));
+ assertFalse(FileUtils.isValidFatFilename(null));
+ assertFalse(FileUtils.isValidFatFilename("."));
+ assertFalse(FileUtils.isValidFatFilename("../foo"));
+ assertFalse(FileUtils.isValidFatFilename("/foo"));
+
+ assertEquals(".._foo", FileUtils.buildValidFatFilename("../foo"));
+ assertEquals("_foo", FileUtils.buildValidFatFilename("/foo"));
+ assertEquals(".foo", FileUtils.buildValidFatFilename(".foo"));
+ assertEquals("foo.bar", FileUtils.buildValidFatFilename("foo.bar"));
+ assertEquals("foo_bar__baz", FileUtils.buildValidFatFilename("foo?bar**baz"));
+ }
+
private void touch(String name, long age) throws Exception {
final File file = new File(mDir, name);
file.createNewFile();
diff --git a/docs/html/about/dashboards/index.jd b/docs/html/about/dashboards/index.jd
index 448dcda..063084d 100644
--- a/docs/html/about/dashboards/index.jd
+++ b/docs/html/about/dashboards/index.jd
@@ -57,7 +57,7 @@ Platform Versions</a>.</p>
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014.
<br/>Any versions with less than 0.1% distribution are not shown.</em>
</p>
@@ -88,7 +88,7 @@ Screens</a>.</p>
</div>
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014.
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014.
<br/>Any screen configurations with less than 0.1% distribution are not shown.</em></p>
@@ -108,7 +108,8 @@ support for any lower version (for example, support for version 2.0 also implies
<img alt="" style="float:right"
-src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chd=t%3A74.7%2C25.3&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+src="//chart.googleapis.com/chart?chl=GL%202.0%7CGL%203.0&chf=bg%2Cs%2C00000000&chd=t%3A72.2%2C27.8&chco=c4df9b%2C6fad0c&cht=p&chs=400x250" />
+
<p>To declare which version of OpenGL ES your application requires, you should use the {@code
android:glEsVersion} attribute of the <a
@@ -126,17 +127,17 @@ uses.</p>
</tr>
<tr>
<td>2.0</td>
-<td>74.7%</td>
+<td>72.2%</td>
</tr>
<tr>
<td>3.0</td>
-<td>25.3%</td>
+<td>27.8%</td>
</tr>
</table>
-<p style="clear:both"><em>Data collected during a 7-day period ending on November 3, 2014</em></p>
+<p style="clear:both"><em>Data collected during a 7-day period ending on December 1, 2014</em></p>
@@ -154,42 +155,42 @@ uses.</p>
var VERSION_DATA =
[
{
- "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chd=t%3A0.6%2C9.8%2C8.5%2C50.9%2C30.2&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
+ "chart": "//chart.googleapis.com/chart?chl=Froyo%7CGingerbread%7CIce%20Cream%20Sandwich%7CJelly%20Bean%7CKitKat&chf=bg%2Cs%2C00000000&chd=t%3A0.5%2C9.1%2C7.8%2C48.7%2C33.9&chco=c4df9b%2C6fad0c&cht=p&chs=500x250",
"data": [
{
"api": 8,
"name": "Froyo",
- "perc": "0.6"
+ "perc": "0.5"
},
{
"api": 10,
"name": "Gingerbread",
- "perc": "9.8"
+ "perc": "9.1"
},
{
"api": 15,
"name": "Ice Cream Sandwich",
- "perc": "8.5"
+ "perc": "7.8"
},
{
"api": 16,
"name": "Jelly Bean",
- "perc": "22.8"
+ "perc": "21.3"
},
{
"api": 17,
"name": "Jelly Bean",
- "perc": "20.8"
+ "perc": "20.4"
},
{
"api": 18,
"name": "Jelly Bean",
- "perc": "7.3"
+ "perc": "7.0"
},
{
"api": 19,
"name": "KitKat",
- "perc": "30.2"
+ "perc": "33.9"
}
]
}
@@ -203,27 +204,28 @@ var SCREEN_DATA =
"Large": {
"hdpi": "0.6",
"ldpi": "0.5",
- "mdpi": "4.5",
- "tvdpi": "1.9",
+ "mdpi": "4.6",
+ "tvdpi": "2.0",
"xhdpi": "0.6"
},
"Normal": {
- "hdpi": "36.6",
- "mdpi": "9.9",
- "xhdpi": "18.9",
- "xxhdpi": "16.0"
+ "hdpi": "36.9",
+ "mdpi": "9.4",
+ "tvdpi": "0.2",
+ "xhdpi": "18.8",
+ "xxhdpi": "16.3"
},
"Small": {
- "ldpi": "5.8"
+ "ldpi": "5.4"
},
"Xlarge": {
"hdpi": "0.3",
- "mdpi": "3.9",
- "xhdpi": "0.5"
+ "mdpi": "3.8",
+ "xhdpi": "0.6"
}
},
- "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chd=t%3A6.3%2C18.3%2C1.9%2C37.5%2C20.0%2C16.0&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
- "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chd=t%3A4.7%2C8.1%2C81.4%2C5.8&chf=bg%2Cs%2C00000000&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
+ "densitychart": "//chart.googleapis.com/chart?chl=ldpi%7Cmdpi%7Ctvdpi%7Chdpi%7Cxhdpi%7Cxxhdpi&chf=bg%2Cs%2C00000000&chd=t%3A5.9%2C17.8%2C2.2%2C37.8%2C20.0%2C16.3&chco=c4df9b%2C6fad0c&cht=p&chs=400x250",
+ "layoutchart": "//chart.googleapis.com/chart?chl=Xlarge%7CLarge%7CNormal%7CSmall&chf=bg%2Cs%2C00000000&chd=t%3A4.7%2C8.3%2C81.6%2C5.4&chco=c4df9b%2C6fad0c&cht=p&chs=400x250"
}
];
diff --git a/docs/html/design/media/wear/ContextualExample.006.png b/docs/html/design/media/wear/ContextualExample.006.png
index 7c3da57a..e680afb 100644
--- a/docs/html/design/media/wear/ContextualExample.006.png
+++ b/docs/html/design/media/wear/ContextualExample.006.png
Binary files differ
diff --git a/docs/html/design/media/wear/ContextualExample.006_2x.png b/docs/html/design/media/wear/ContextualExample.006_2x.png
index 319530d..ee4087e 100644
--- a/docs/html/design/media/wear/ContextualExample.006_2x.png
+++ b/docs/html/design/media/wear/ContextualExample.006_2x.png
Binary files differ
diff --git a/docs/html/design/wear/context.jd b/docs/html/design/wear/context.jd
index 2e66532..688806f 100644
--- a/docs/html/design/wear/context.jd
+++ b/docs/html/design/wear/context.jd
@@ -131,7 +131,7 @@ app.</p>
<div class="slide">
<h2>Zoo</h2>
-<p>Notifies visitors when the penguins are going to be fed!
+<p>Notifies visitors when the penguins are going to be fed.
</p>
<img src="{@docRoot}design/media/wear/ContextualExample.014.png" alt=""
srcset="{@docRoot}design/media/wear/ContextualExample.014.png 1x,
diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp
index 80b4c2a..075f2c5 100755
--- a/libs/hwui/OpenGLRenderer.cpp
+++ b/libs/hwui/OpenGLRenderer.cpp
@@ -1560,13 +1560,7 @@ void OpenGLRenderer::setupDraw(bool clearLayer) {
setScissorFromClip();
}
- if (clearLayer) {
- setStencilFromClip();
- } else {
- // While clearing layer, force disable stencil buffer, since
- // it's invalid to stencil-clip *during* the layer clear
- mCaches.stencil.disable();
- }
+ setStencilFromClip();
}
mDescription.reset();
diff --git a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
index 066acac..073d9c7 100644
--- a/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
+++ b/packages/ExternalStorageProvider/src/com/android/externalstorage/ExternalStorageProvider.java
@@ -43,6 +43,7 @@ import android.util.Log;
import android.webkit.MimeTypeMap;
import com.android.internal.annotations.GuardedBy;
+import com.android.internal.annotations.VisibleForTesting;
import com.google.android.collect.Lists;
import com.google.android.collect.Maps;
@@ -53,6 +54,7 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
+import java.util.Objects;
public class ExternalStorageProvider extends DocumentsProvider {
private static final String TAG = "ExternalStorage";
@@ -313,27 +315,19 @@ public class ExternalStorageProvider extends DocumentsProvider {
@Override
public String createDocument(String docId, String mimeType, String displayName)
throws FileNotFoundException {
+ displayName = FileUtils.buildValidFatFilename(displayName);
+
final File parent = getFileForDocId(docId);
if (!parent.isDirectory()) {
throw new IllegalArgumentException("Parent document isn't a directory");
}
- File file;
+ final File file = buildUniqueFile(parent, mimeType, displayName);
if (Document.MIME_TYPE_DIR.equals(mimeType)) {
- file = new File(parent, displayName);
if (!file.mkdir()) {
throw new IllegalStateException("Failed to mkdir " + file);
}
} else {
- displayName = removeExtension(mimeType, displayName);
- file = new File(parent, addExtension(mimeType, displayName));
-
- // If conflicting file, try adding counter suffix
- int n = 0;
- while (file.exists() && n++ < 32) {
- file = new File(parent, addExtension(mimeType, displayName + " (" + n + ")"));
- }
-
try {
if (!file.createNewFile()) {
throw new IllegalStateException("Failed to touch " + file);
@@ -342,11 +336,78 @@ public class ExternalStorageProvider extends DocumentsProvider {
throw new IllegalStateException("Failed to touch " + file + ": " + e);
}
}
+
return getDocIdForFile(file);
}
+ private static File buildFile(File parent, String name, String ext) {
+ if (TextUtils.isEmpty(ext)) {
+ return new File(parent, name);
+ } else {
+ return new File(parent, name + "." + ext);
+ }
+ }
+
+ @VisibleForTesting
+ public static File buildUniqueFile(File parent, String mimeType, String displayName)
+ throws FileNotFoundException {
+ String name;
+ String ext;
+
+ if (Document.MIME_TYPE_DIR.equals(mimeType)) {
+ name = displayName;
+ ext = null;
+ } else {
+ String mimeTypeFromExt;
+
+ // Extract requested extension from display name
+ final int lastDot = displayName.lastIndexOf('.');
+ if (lastDot >= 0) {
+ name = displayName.substring(0, lastDot);
+ ext = displayName.substring(lastDot + 1);
+ mimeTypeFromExt = MimeTypeMap.getSingleton().getMimeTypeFromExtension(
+ ext.toLowerCase());
+ } else {
+ name = displayName;
+ ext = null;
+ mimeTypeFromExt = null;
+ }
+
+ if (mimeTypeFromExt == null) {
+ mimeTypeFromExt = "application/octet-stream";
+ }
+
+ final String extFromMimeType = MimeTypeMap.getSingleton().getExtensionFromMimeType(
+ mimeType);
+ if (Objects.equals(mimeType, mimeTypeFromExt) || Objects.equals(ext, extFromMimeType)) {
+ // Extension maps back to requested MIME type; allow it
+ } else {
+ // No match; insist that create file matches requested MIME
+ name = displayName;
+ ext = extFromMimeType;
+ }
+ }
+
+ File file = buildFile(parent, name, ext);
+
+ // If conflicting file, try adding counter suffix
+ int n = 0;
+ while (file.exists()) {
+ if (n++ >= 32) {
+ throw new FileNotFoundException("Failed to create unique file");
+ }
+ file = buildFile(parent, name + " (" + n + ")", ext);
+ }
+
+ return file;
+ }
+
@Override
public String renameDocument(String docId, String displayName) throws FileNotFoundException {
+ // Since this provider treats renames as generating a completely new
+ // docId, we're okay with letting the MIME type change.
+ displayName = FileUtils.buildValidFatFilename(displayName);
+
final File before = getFileForDocId(docId);
final File after = new File(before.getParentFile(), displayName);
if (after.exists()) {
@@ -482,34 +543,6 @@ public class ExternalStorageProvider extends DocumentsProvider {
return "application/octet-stream";
}
- /**
- * Remove file extension from name, but only if exact MIME type mapping
- * exists. This means we can reapply the extension later.
- */
- private static String removeExtension(String mimeType, String name) {
- final int lastDot = name.lastIndexOf('.');
- if (lastDot >= 0) {
- final String extension = name.substring(lastDot + 1).toLowerCase();
- final String nameMime = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
- if (mimeType.equals(nameMime)) {
- return name.substring(0, lastDot);
- }
- }
- return name;
- }
-
- /**
- * Add file extension to name, but only if exact MIME type mapping exists.
- */
- private static String addExtension(String mimeType, String name) {
- final String extension = MimeTypeMap.getSingleton()
- .getExtensionFromMimeType(mimeType);
- if (extension != null) {
- return name + "." + extension;
- }
- return name;
- }
-
private void startObserving(File file, Uri notifyUri) {
synchronized (mObservers) {
DirectoryObserver observer = mObservers.get(file);
diff --git a/packages/ExternalStorageProvider/tests/Android.mk b/packages/ExternalStorageProvider/tests/Android.mk
new file mode 100644
index 0000000..830731a
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/Android.mk
@@ -0,0 +1,16 @@
+
+LOCAL_PATH := $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_MODULE_TAGS := tests
+
+LOCAL_SRC_FILES := $(call all-java-files-under, src)
+
+LOCAL_JAVA_LIBRARIES := android.test.runner
+
+LOCAL_PACKAGE_NAME := ExternalStorageProviderTests
+LOCAL_INSTRUMENTATION_FOR := ExternalStorageProvider
+
+LOCAL_CERTIFICATE := platform
+
+include $(BUILD_PACKAGE)
diff --git a/packages/ExternalStorageProvider/tests/AndroidManifest.xml b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
new file mode 100644
index 0000000..ffcd499
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/AndroidManifest.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="com.android.externalstorage.tests">
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="android.test.InstrumentationTestRunner"
+ android:targetPackage="com.android.externalstorage"
+ android:label="Tests for ExternalStorageProvider" />
+
+</manifest>
diff --git a/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
new file mode 100644
index 0000000..f980b60
--- /dev/null
+++ b/packages/ExternalStorageProvider/tests/src/com/android/externalstorage/ExternalStorageProviderTest.java
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT 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.externalstorage;
+
+import static com.android.externalstorage.ExternalStorageProvider.buildUniqueFile;
+
+import android.os.FileUtils;
+import android.provider.DocumentsContract.Document;
+import android.test.AndroidTestCase;
+import android.test.suitebuilder.annotation.MediumTest;
+
+import java.io.File;
+
+@MediumTest
+public class ExternalStorageProviderTest extends AndroidTestCase {
+
+ private File mTarget;
+
+ @Override
+ protected void setUp() throws Exception {
+ super.setUp();
+ mTarget = getContext().getFilesDir();
+ FileUtils.deleteContents(mTarget);
+ }
+
+ @Override
+ protected void tearDown() throws Exception {
+ super.tearDown();
+ FileUtils.deleteContents(mTarget);
+ }
+
+ public void testBuildUniqueFile_normal() throws Exception {
+ assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test"));
+ assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ assertNameEquals("test.jpeg", buildUniqueFile(mTarget, "image/jpeg", "test.jpeg"));
+ assertNameEquals("TEst.JPeg", buildUniqueFile(mTarget, "image/jpeg", "TEst.JPeg"));
+ assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png.jpg"));
+ assertNameEquals("test.png.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.png"));
+
+ assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test"));
+ assertNameEquals("test.flac", buildUniqueFile(mTarget, "audio/flac", "test.flac"));
+ assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test"));
+ assertNameEquals("test.flac", buildUniqueFile(mTarget, "application/x-flac", "test.flac"));
+ }
+
+ public void testBuildUniqueFile_unknown() throws Exception {
+ assertNameEquals("test", buildUniqueFile(mTarget, "application/octet-stream", "test"));
+ assertNameEquals("test.jpg", buildUniqueFile(mTarget, "application/octet-stream", "test.jpg"));
+ assertNameEquals(".test", buildUniqueFile(mTarget, "application/octet-stream", ".test"));
+
+ assertNameEquals("test", buildUniqueFile(mTarget, "lolz/lolz", "test"));
+ assertNameEquals("test.lolz", buildUniqueFile(mTarget, "lolz/lolz", "test.lolz"));
+ }
+
+ public void testBuildUniqueFile_dir() throws Exception {
+ assertNameEquals("test", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+ new File(mTarget, "test").mkdir();
+ assertNameEquals("test (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test"));
+
+ assertNameEquals("test.jpg", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ new File(mTarget, "test.jpg").mkdir();
+ assertNameEquals("test.jpg (1)", buildUniqueFile(mTarget, Document.MIME_TYPE_DIR, "test.jpg"));
+ }
+
+ public void testBuildUniqueFile_increment() throws Exception {
+ assertNameEquals("test.jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test.jpg").createNewFile();
+ assertNameEquals("test (1).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ new File(mTarget, "test (1).jpg").createNewFile();
+ assertNameEquals("test (2).jpg", buildUniqueFile(mTarget, "image/jpeg", "test.jpg"));
+ }
+
+ private static void assertNameEquals(String expected, File actual) {
+ assertEquals(expected, actual.getName());
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
index f9a68d0..479c982 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/AccessPointControllerImpl.java
@@ -67,6 +67,7 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
private final UserManager mUserManager;
private final Receiver mReceiver = new Receiver();
+ private NetworkControllerImpl mNetworkController;
private boolean mScanning;
private int mCurrentUser;
@@ -77,6 +78,10 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
mCurrentUser = ActivityManager.getCurrentUser();
}
+ void setNetworkController(NetworkControllerImpl networkController) {
+ mNetworkController = networkController;
+ }
+
public boolean canConfigWifi() {
return !mUserManager.hasUserRestriction(UserManager.DISALLOW_CONFIG_WIFI,
new UserHandle(mCurrentUser));
@@ -181,7 +186,6 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
ap.isConfigured = config != null;
ap.networkId = config != null ? config.networkId : AccessPoint.NO_NETWORK;
ap.ssid = ssid;
- ap.iconId = ICONS[level];
// Connected if either:
// -The network ID in the active WifiInfo matches this network's ID.
// -The network is ephemeral (no configuration) but the SSID matches.
@@ -189,7 +193,13 @@ public class AccessPointControllerImpl implements NetworkController.AccessPointC
&& ap.networkId == connectedNetworkId) ||
(ap.networkId == WifiConfiguration.INVALID_NETWORK_ID && wifiInfo != null &&
ap.ssid.equals(trimDoubleQuotes(wifiInfo.getSSID())));
- ap.level = level;
+ if (ap.isConnected && mNetworkController != null) {
+ // Ensure we have the connected network's RSSI.
+ ap.level = mNetworkController.getConnectedWifiLevel();
+ } else {
+ ap.level = level;
+ }
+ ap.iconId = ICONS[ap.level];
// Based on Settings AccessPoint#getSecurity, keep up to date
// with better methods of determining no security or not.
ap.hasSecurity = scanResult.capabilities.contains("WEP")
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
index 5a97c75..f3a04b6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/policy/NetworkControllerImpl.java
@@ -82,7 +82,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
final WifiSignalController mWifiSignalController;
@VisibleForTesting
final MobileSignalController mMobileSignalController;
- private final AccessPointController mAccessPoints;
+ private final AccessPointControllerImpl mAccessPoints;
private final MobileDataControllerImpl mMobileDataController;
// bluetooth
@@ -154,6 +154,7 @@ public class NetworkControllerImpl extends BroadcastReceiver
// AIRPLANE_MODE_CHANGED is sent at boot; we've probably already missed it
updateAirplaneMode(true);
+ mAccessPoints.setNetworkController(this);
}
private void registerListeners() {
@@ -178,6 +179,10 @@ public class NetworkControllerImpl extends BroadcastReceiver
mContext.unregisterReceiver(this);
}
+ public int getConnectedWifiLevel() {
+ return mWifiSignalController.getState().level;
+ }
+
@Override
public AccessPointController getAccessPointController() {
return mAccessPoints;
diff --git a/services/core/java/com/android/server/ConnectivityService.java b/services/core/java/com/android/server/ConnectivityService.java
index 8b3739d..2e64640 100644
--- a/services/core/java/com/android/server/ConnectivityService.java
+++ b/services/core/java/com/android/server/ConnectivityService.java
@@ -3768,8 +3768,8 @@ public class ConnectivityService extends IConnectivityManager.Stub
int notificationType) {
if (notificationType == ConnectivityManager.CALLBACK_AVAILABLE) {
Intent intent = new Intent();
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK, nri.request);
- intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, networkAgent.network);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK, networkAgent.network);
+ intent.putExtra(ConnectivityManager.EXTRA_NETWORK_REQUEST, nri.request);
sendIntent(nri.mPendingIntent, intent);
}
// else not handled
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 3e6d15a..57a89679 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -4285,6 +4285,14 @@ public class PackageManagerService extends IPackageManager.Stub {
boolean updatedPkgBetter = false;
// First check if this is a system package that may involve an update
if (updatedPkg != null && (parseFlags&PackageParser.PARSE_IS_SYSTEM) != 0) {
+ // If new package is not located in "/system/priv-app" (e.g. due to an OTA),
+ // it needs to drop FLAG_PRIVILEGED.
+ if (locationIsPrivileged(scanFile)) {
+ updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;
+ } else {
+ updatedPkg.pkgFlags &= ~ApplicationInfo.FLAG_PRIVILEGED;
+ }
+
if (ps != null && !ps.codePath.equals(scanFile)) {
// The path has changed from what was last scanned... check the
// version of the new path against what we have stored to determine
@@ -4302,12 +4310,6 @@ public class PackageManagerService extends IPackageManager.Stub {
+ " to " + scanFile);
updatedPkg.codePath = scanFile;
updatedPkg.codePathString = scanFile.toString();
- // This is the point at which we know that the system-disk APK
- // for this package has moved during a reboot (e.g. due to an OTA),
- // so we need to reevaluate it for privilege policy.
- if (locationIsPrivileged(scanFile)) {
- updatedPkg.pkgFlags |= ApplicationInfo.FLAG_PRIVILEGED;
- }
}
updatedPkg.pkg = pkg;
throw new PackageManagerException(INSTALL_FAILED_DUPLICATE_PACKAGE, null);
@@ -7202,7 +7204,9 @@ public class PackageManagerService extends IPackageManager.Stub {
// If the original was granted this permission, we take
// that grant decision as read and propagate it to the
// update.
- allowed = true;
+ if (sysPs.isPrivileged()) {
+ allowed = true;
+ }
} else {
// The system apk may have been updated with an older
// version of the one on the data partition, but which
diff --git a/services/core/java/com/android/server/pm/UserManagerService.java b/services/core/java/com/android/server/pm/UserManagerService.java
index db0f53b..be3251c 100644
--- a/services/core/java/com/android/server/pm/UserManagerService.java
+++ b/services/core/java/com/android/server/pm/UserManagerService.java
@@ -224,6 +224,7 @@ public class UserManagerService extends IUserManager.Stub {
|FileUtils.S_IROTH|FileUtils.S_IXOTH,
-1, -1);
mUserListFile = new File(mUsersDir, USER_LIST_FILENAME);
+ initDefaultGuestRestrictions();
readUserListLocked();
// Prune out any partially created/partially removed users.
ArrayList<UserInfo> partials = new ArrayList<UserInfo>();
@@ -469,7 +470,7 @@ public class UserManagerService extends IUserManager.Stub {
private void initDefaultGuestRestrictions() {
if (mGuestRestrictions.isEmpty()) {
mGuestRestrictions.putBoolean(UserManager.DISALLOW_OUTGOING_CALLS, true);
- writeUserListLocked();
+ mGuestRestrictions.putBoolean(UserManager.DISALLOW_SMS, true);
}
}
@@ -653,8 +654,15 @@ public class UserManagerService extends IUserManager.Stub {
}
}
} else if (name.equals(TAG_GUEST_RESTRICTIONS)) {
- mGuestRestrictions.clear();
- readRestrictionsLocked(parser, mGuestRestrictions);
+ while ((type = parser.next()) != XmlPullParser.END_DOCUMENT
+ && type != XmlPullParser.END_TAG) {
+ if (type == XmlPullParser.START_TAG) {
+ if (parser.getName().equals(TAG_RESTRICTIONS)) {
+ readRestrictionsLocked(parser, mGuestRestrictions);
+ }
+ break;
+ }
+ }
}
}
}