Merge changes I8e2ed73d,I42aeb522
* changes:
Implement the request and recommend calls.
Implement the discovery of a network recommendation provider.
diff --git a/api/current.txt b/api/current.txt
index 89573ac..395d7aa 100644
--- a/api/current.txt
+++ b/api/current.txt
@@ -12733,7 +12733,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/api/system-current.txt b/api/system-current.txt
index 870070b..8bedf98 100644
--- a/api/system-current.txt
+++ b/api/system-current.txt
@@ -13226,7 +13226,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/api/test-current.txt b/api/test-current.txt
index 10554ff..e43d1b5 100644
--- a/api/test-current.txt
+++ b/api/test-current.txt
@@ -12764,7 +12764,6 @@
field public static final int RGBA_8888 = 1; // 0x1
field public static final int RGBA_F16 = 22; // 0x16
field public static final int RGBX_8888 = 2; // 0x2
- field public static final int RGBX_F16 = 23; // 0x17
field public static final deprecated int RGB_332 = 11; // 0xb
field public static final int RGB_565 = 4; // 0x4
field public static final int RGB_888 = 3; // 0x3
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 74aded6..32bf66a 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -3789,6 +3789,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestActivity_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestActivity_visibleToEphemeral, false);
@@ -3827,7 +3829,7 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in intent filter at "
+ mArchiveSourcePath + " "
@@ -3835,6 +3837,10 @@
} else {
a.intents.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (!receiver && parser.getName().equals("preferred")) {
ActivityIntentInfo intent = new ActivityIntentInfo(a);
if (!parseIntent(res, parser, false /*allowGlobs*/, false /*allowAutoVerify*/,
@@ -3842,7 +3848,7 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
if (intent.countActions() == 0) {
Slog.w(TAG, "No actions in preferred at "
+ mArchiveSourcePath + " "
@@ -3853,6 +3859,10 @@
}
owner.preferredActivityFilters.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData = parseMetaData(res, parser, a.metaData,
outError)) == null) {
@@ -4093,6 +4103,7 @@
}
}
+ // TODO add visibleToInstantApp attribute to activity alias
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| ((a.info.flags & ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL) != 0);
@@ -4124,9 +4135,14 @@
+ parser.getPositionDescription());
} else {
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral
+ || isWebBrowsableIntent(intent));
a.intents.add(intent);
}
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (intent.isVisibleToEphemeral()) {
+ a.info.flags |= ActivityInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((a.metaData=parseMetaData(res, parser, a.metaData,
outError)) == null) {
@@ -4262,6 +4278,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestProvider_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestProvider_visibleToEphemeral, false);
@@ -4291,7 +4309,8 @@
}
p.info.authority = cpname.intern();
- if (!parseProviderTags(res, parser, isEphemeral, visibleToEphemeral, p, outError)) {
+ if (!parseProviderTags(
+ res, parser, isEphemeral, hasVisibleToEphemeral, visibleToEphemeral, p, outError)) {
return null;
}
@@ -4299,8 +4318,9 @@
}
private boolean parseProviderTags(Resources res, XmlResourceParser parser,
- boolean isEphemeral, boolean visibleToEphemeral, Provider outInfo, String[] outError)
- throws XmlPullParserException, IOException {
+ boolean isEphemeral, boolean hasVisibleToEphemeral, boolean visibleToEphemeral,
+ Provider outInfo, String[] outError)
+ throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
int type;
while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
@@ -4317,8 +4337,12 @@
return false;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
outInfo.intents.add(intent);
+ // adjust provider flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ outInfo.info.flags |= ProviderInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
} else if (parser.getName().equals("meta-data")) {
if ((outInfo.metaData=parseMetaData(res, parser,
@@ -4565,6 +4589,8 @@
ApplicationInfo.PRIVATE_FLAG_PARTIALLY_DIRECT_BOOT_AWARE;
}
+ final boolean hasVisibleToEphemeral =
+ sa.hasValue(R.styleable.AndroidManifestService_visibleToEphemeral);
final boolean isEphemeral = ((flags & PARSE_IS_EPHEMERAL) != 0);
final boolean visibleToEphemeral = isEphemeral
|| sa.getBoolean(R.styleable.AndroidManifestService_visibleToEphemeral, false);
@@ -4600,8 +4626,11 @@
return null;
}
intent.setEphemeral(isEphemeral);
- intent.setVisibleToEphemeral(visibleToEphemeral);
-
+ intent.setVisibleToEphemeral(visibleToEphemeral || isWebBrowsableIntent(intent));
+ // adjust activity flags when we implicitly expose it via a browsable filter
+ if (!hasVisibleToEphemeral && intent.isVisibleToEphemeral()) {
+ s.info.flags |= ServiceInfo.FLAG_VISIBLE_TO_EPHEMERAL;
+ }
s.intents.add(intent);
} else if (parser.getName().equals("meta-data")) {
if ((s.metaData=parseMetaData(res, parser, s.metaData,
@@ -4629,6 +4658,12 @@
return s;
}
+ private boolean isWebBrowsableIntent(IntentInfo intent) {
+ return intent.hasAction(Intent.ACTION_VIEW)
+ && intent.hasCategory(Intent.CATEGORY_BROWSABLE)
+ && (intent.hasDataScheme("http") || intent.hasDataScheme("https"));
+ }
+
private boolean parseAllMetaData(Resources res, XmlResourceParser parser, String tag,
Component<?> outInfo, String[] outError) throws XmlPullParserException, IOException {
int outerDepth = parser.getDepth();
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
index c83298b..7340cf7 100644
--- a/core/java/android/view/ViewGroup.java
+++ b/core/java/android/view/ViewGroup.java
@@ -869,10 +869,13 @@
*/
@Override
public View focusSearch(View focused, int direction) {
- if (isRootNamespace()) {
+ if (isRootNamespace()
+ || isKeyboardNavigationCluster()
+ && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD)) {
// root namespace means we should consider ourselves the top of the
// tree for focus searching; otherwise we could be focus searching
- // into other tabs. see LocalActivityManager and TabHost for more info
+ // into other tabs. see LocalActivityManager and TabHost for more info.
+ // Cluster's root works same way for the forward and backward navigation.
return FocusFinder.getInstance().findNextFocus(this, focused, direction);
} else if (mParent != null) {
return mParent.focusSearch(focused, direction);
@@ -1104,6 +1107,12 @@
@Override
public void addFocusables(ArrayList<View> views, int direction, int focusableMode) {
+ if (isKeyboardNavigationCluster()
+ && (direction == FOCUS_FORWARD || direction == FOCUS_BACKWARD) && !hasFocus()) {
+ // A cluster cannot be focus-entered from outside using forward/backward navigation.
+ return;
+ }
+
final int focusableCount = views.size();
final int descendantFocusability = getDescendantFocusability();
@@ -3026,7 +3035,8 @@
final View[] children = mChildren;
for (int i = index; i != end; i += increment) {
View child = children[i];
- if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+ if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+ && !child.isKeyboardNavigationCluster()) {
if (child.requestFocus(direction, previouslyFocusedRect)) {
return true;
}
diff --git a/core/java/com/android/internal/os/WebViewZygoteInit.java b/core/java/com/android/internal/os/WebViewZygoteInit.java
index d968e3c..a8a5549 100644
--- a/core/java/com/android/internal/os/WebViewZygoteInit.java
+++ b/core/java/com/android/internal/os/WebViewZygoteInit.java
@@ -62,6 +62,9 @@
ClassLoader loader = ApplicationLoaders.getDefault().createAndCacheWebViewClassLoader(
packagePath, libsPath);
+ // Add the APK to the Zygote's list of allowed files for children.
+ Zygote.nativeAllowFileAcrossFork(packagePath);
+
// Once we have the classloader, look up the WebViewFactoryProvider implementation and
// call preloadInZygote() on it to give it the opportunity to preload the native library
// and perform any other initialisation work that should be shared among the children.
diff --git a/core/java/com/android/internal/os/Zygote.java b/core/java/com/android/internal/os/Zygote.java
index fc0ccb7..293de3d 100644
--- a/core/java/com/android/internal/os/Zygote.java
+++ b/core/java/com/android/internal/os/Zygote.java
@@ -153,6 +153,11 @@
int[][] rlimits, long permittedCapabilities, long effectiveCapabilities);
/**
+ * Lets children of the zygote inherit open file descriptors to this path.
+ */
+ native protected static void nativeAllowFileAcrossFork(String path);
+
+ /**
* Zygote unmount storage space on initializing.
* This method is called once.
*/
diff --git a/core/jni/Android.mk b/core/jni/Android.mk
index 35c9ee3..3555057 100644
--- a/core/jni/Android.mk
+++ b/core/jni/Android.mk
@@ -188,6 +188,7 @@
com_android_internal_util_VirtualRefBasePtr.cpp \
com_android_internal_view_animation_NativeInterpolatorFactoryHelper.cpp \
hwbinder/EphemeralStorage.cpp \
+ fd_utils.cpp \
LOCAL_C_INCLUDES += \
$(LOCAL_PATH)/include \
diff --git a/core/jni/android/graphics/Bitmap.cpp b/core/jni/android/graphics/Bitmap.cpp
index b656bb0..80711aa 100755
--- a/core/jni/android/graphics/Bitmap.cpp
+++ b/core/jni/android/graphics/Bitmap.cpp
@@ -1190,8 +1190,7 @@
}
}
-static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle,
- jlong bm1Handle) {
+static jboolean Bitmap_sameAs(JNIEnv* env, jobject, jlong bm0Handle, jlong bm1Handle) {
SkBitmap bm0;
SkBitmap bm1;
@@ -1207,11 +1206,11 @@
bitmap0->bitmap().getSkBitmap(&bm0);
bitmap1->bitmap().getSkBitmap(&bm1);
- if (bm0.width() != bm1.width() ||
- bm0.height() != bm1.height() ||
- bm0.colorType() != bm1.colorType() ||
- bm0.alphaType() != bm1.alphaType() ||
- bm0.colorSpace() != bm1.colorSpace()) {
+ if (bm0.width() != bm1.width()
+ || bm0.height() != bm1.height()
+ || bm0.colorType() != bm1.colorType()
+ || bm0.alphaType() != bm1.alphaType()
+ || !SkColorSpace::Equals(bm0.colorSpace(), bm1.colorSpace())) {
return JNI_FALSE;
}
diff --git a/core/jni/android_view_GraphicBuffer.cpp b/core/jni/android_view_GraphicBuffer.cpp
index bb69e27..f18837f 100644
--- a/core/jni/android_view_GraphicBuffer.cpp
+++ b/core/jni/android_view_GraphicBuffer.cpp
@@ -144,8 +144,6 @@
return kN32_SkColorType;
case PIXEL_FORMAT_RGBA_FP16:
return kRGBA_F16_SkColorType;
- case PIXEL_FORMAT_RGBX_FP16:
- return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGB_565:
return kRGB_565_SkColorType;
default:
diff --git a/core/jni/android_view_Surface.cpp b/core/jni/android_view_Surface.cpp
index a5b7671..96e6f81 100644
--- a/core/jni/android_view_Surface.cpp
+++ b/core/jni/android_view_Surface.cpp
@@ -169,7 +169,6 @@
case HAL_PIXEL_FORMAT_RGBA_8888:
case HAL_PIXEL_FORMAT_RGBX_8888:
case HAL_PIXEL_FORMAT_RGBA_FP16:
- case HAL_PIXEL_FORMAT_RGBX_FP16:
case HAL_PIXEL_FORMAT_RGB_888:
case HAL_PIXEL_FORMAT_RGB_565:
case HAL_PIXEL_FORMAT_Y8:
@@ -285,7 +284,6 @@
switch (format) {
case PIXEL_FORMAT_RGBX_8888: return kN32_SkColorType;
case PIXEL_FORMAT_RGBA_8888: return kN32_SkColorType;
- case PIXEL_FORMAT_RGBX_FP16: return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGBA_FP16: return kRGBA_F16_SkColorType;
case PIXEL_FORMAT_RGB_565: return kRGB_565_SkColorType;
default: return kUnknown_SkColorType;
diff --git a/core/jni/android_view_SurfaceControl.cpp b/core/jni/android_view_SurfaceControl.cpp
index 62c3d04..ed071cd 100644
--- a/core/jni/android_view_SurfaceControl.cpp
+++ b/core/jni/android_view_SurfaceControl.cpp
@@ -164,11 +164,6 @@
alphaType = kPremul_SkAlphaType;
break;
}
- case PIXEL_FORMAT_RGBX_FP16: {
- colorType = kRGBA_F16_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
- }
case PIXEL_FORMAT_RGBA_FP16: {
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
diff --git a/core/jni/android_view_TextureView.cpp b/core/jni/android_view_TextureView.cpp
index d3c0202..613e040 100644
--- a/core/jni/android_view_TextureView.cpp
+++ b/core/jni/android_view_TextureView.cpp
@@ -87,10 +87,6 @@
colorType = kRGBA_F16_SkColorType;
alphaType = kPremul_SkAlphaType;
break;
- case WINDOW_FORMAT_RGBX_FP16:
- colorType = kRGBA_F16_SkColorType;
- alphaType = kOpaque_SkAlphaType;
- break;
case WINDOW_FORMAT_RGB_565:
colorType = kRGB_565_SkColorType;
alphaType = kOpaque_SkAlphaType;
diff --git a/core/jni/com_android_internal_os_Zygote.cpp b/core/jni/com_android_internal_os_Zygote.cpp
index da059e3..cc7b958 100644
--- a/core/jni/com_android_internal_os_Zygote.cpp
+++ b/core/jni/com_android_internal_os_Zygote.cpp
@@ -56,7 +56,7 @@
#include "ScopedLocalRef.h"
#include "ScopedPrimitiveArray.h"
#include "ScopedUtfChars.h"
-#include "fd_utils-inl.h"
+#include "fd_utils.h"
#include "nativebridge/native_bridge.h"
@@ -709,6 +709,16 @@
return pid;
}
+static void com_android_internal_os_Zygote_nativeAllowFileAcrossFork(
+ JNIEnv* env, jclass, jstring path) {
+ ScopedUtfChars path_native(env, path);
+ const char* path_cstr = path_native.c_str();
+ if (!path_cstr) {
+ RuntimeAbort(env, __LINE__, "path_cstr == NULL");
+ }
+ FileDescriptorWhitelist::Get()->Allow(path_cstr);
+}
+
static void com_android_internal_os_Zygote_nativeUnmountStorageOnInit(JNIEnv* env, jclass) {
// Zygote process unmount root storage space initially before every child processes are forked.
// Every forked child processes (include SystemServer) only mount their own root storage space
@@ -753,6 +763,8 @@
(void *) com_android_internal_os_Zygote_nativeForkAndSpecialize },
{ "nativeForkSystemServer", "(II[II[[IJJ)I",
(void *) com_android_internal_os_Zygote_nativeForkSystemServer },
+ { "nativeAllowFileAcrossFork", "(Ljava/lang/String;)V",
+ (void *) com_android_internal_os_Zygote_nativeAllowFileAcrossFork },
{ "nativeUnmountStorageOnInit", "()V",
(void *) com_android_internal_os_Zygote_nativeUnmountStorageOnInit }
};
diff --git a/core/jni/fd_utils-inl.h b/core/jni/fd_utils-inl.h
deleted file mode 100644
index e270911..0000000
--- a/core/jni/fd_utils-inl.h
+++ /dev/null
@@ -1,587 +0,0 @@
-/*
- * Copyright (C) 2016 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#include <string>
-#include <unordered_map>
-#include <set>
-#include <vector>
-#include <algorithm>
-
-#include <android-base/strings.h>
-#include <dirent.h>
-#include <fcntl.h>
-#include <grp.h>
-#include <inttypes.h>
-#include <stdlib.h>
-#include <sys/socket.h>
-#include <sys/stat.h>
-#include <sys/types.h>
-#include <sys/un.h>
-#include <unistd.h>
-
-#include <cutils/log.h>
-#include "JNIHelp.h"
-#include "ScopedPrimitiveArray.h"
-
-// Whitelist of open paths that the zygote is allowed to keep open.
-//
-// In addition to the paths listed here, all files ending with
-// ".jar" under /system/framework" are whitelisted. See
-// FileDescriptorInfo::IsWhitelisted for the canonical definition.
-//
-// If the whitelisted path is associated with a regular file or a
-// character device, the file is reopened after a fork with the same
-// offset and mode. If the whilelisted path is associated with a
-// AF_UNIX socket, the socket will refer to /dev/null after each
-// fork, and all operations on it will fail.
-static const char* kPathWhitelist[] = {
- "/dev/null",
- "/dev/socket/zygote",
- "/dev/socket/zygote_secondary",
- "/dev/socket/webview_zygote",
- "/sys/kernel/debug/tracing/trace_marker",
- "/system/framework/framework-res.apk",
- "/dev/urandom",
- "/dev/ion",
- "/dev/dri/renderD129", // Fixes b/31172436
-};
-
-static const char* kFdPath = "/proc/self/fd";
-
-// Keeps track of all relevant information (flags, offset etc.) of an
-// open zygote file descriptor.
-class FileDescriptorInfo {
- public:
- // Create a FileDescriptorInfo for a given file descriptor. Returns
- // |NULL| if an error occurred.
- static FileDescriptorInfo* createFromFd(int fd) {
- struct stat f_stat;
- // This should never happen; the zygote should always have the right set
- // of permissions required to stat all its open files.
- if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
- ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
- return NULL;
- }
-
- if (S_ISSOCK(f_stat.st_mode)) {
- std::string socket_name;
- if (!GetSocketName(fd, &socket_name)) {
- return NULL;
- }
-
- if (!IsWhitelisted(socket_name)) {
- ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
- return NULL;
- }
-
- return new FileDescriptorInfo(fd);
- }
-
- // We only handle whitelisted regular files and character devices. Whitelisted
- // character devices must provide a guarantee of sensible behaviour when
- // reopened.
- //
- // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
- // S_ISLINK : Not supported.
- // S_ISBLK : Not supported.
- // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
- // with the child process across forks but those should have been closed
- // before we got to this point.
- if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
- ALOGE("Unsupported st_mode %d", f_stat.st_mode);
- return NULL;
- }
-
- std::string file_path;
- if (!Readlink(fd, &file_path)) {
- return NULL;
- }
-
- if (!IsWhitelisted(file_path)) {
- ALOGE("Not whitelisted : %s", file_path.c_str());
- return NULL;
- }
-
- // File descriptor flags : currently on FD_CLOEXEC. We can set these
- // using F_SETFD - we're single threaded at this point of execution so
- // there won't be any races.
- const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
- if (fd_flags == -1) {
- ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
- return NULL;
- }
-
- // File status flags :
- // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
- // to the open() call.
- //
- // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
- // do about these, since the file has already been created. We shall ignore
- // them here.
- //
- // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
- // can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
- // In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
- // their presence and pass them in to open().
- int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
- if (fs_flags == -1) {
- ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
- return NULL;
- }
-
- // File offset : Ignore the offset for non seekable files.
- const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
-
- // We pass the flags that open accepts to open, and use F_SETFL for
- // the rest of them.
- static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
- int open_flags = fs_flags & (kOpenFlags);
- fs_flags = fs_flags & (~(kOpenFlags));
-
- return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
- }
-
- // Checks whether the file descriptor associated with this object
- // refers to the same description.
- bool Restat() const {
- struct stat f_stat;
- if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
- return false;
- }
-
- return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
- }
-
- bool ReopenOrDetach() const {
- if (is_sock) {
- return DetachSocket();
- }
-
- // NOTE: This might happen if the file was unlinked after being opened.
- // It's a common pattern in the case of temporary files and the like but
- // we should not allow such usage from the zygote.
- const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
-
- if (new_fd == -1) {
- ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
- close(new_fd);
- ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
- close(new_fd);
- ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
- return false;
- }
-
- if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
- close(new_fd);
- ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
- return false;
- }
-
- if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
- close(new_fd);
- ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
- return false;
- }
-
- close(new_fd);
-
- return true;
- }
-
- const int fd;
- const struct stat stat;
- const std::string file_path;
- const int open_flags;
- const int fd_flags;
- const int fs_flags;
- const off_t offset;
- const bool is_sock;
-
- private:
- FileDescriptorInfo(int fd) :
- fd(fd),
- stat(),
- open_flags(0),
- fd_flags(0),
- fs_flags(0),
- offset(0),
- is_sock(true) {
- }
-
- FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
- int fd_flags, int fs_flags, off_t offset) :
- fd(fd),
- stat(stat),
- file_path(file_path),
- open_flags(open_flags),
- fd_flags(fd_flags),
- fs_flags(fs_flags),
- offset(offset),
- is_sock(false) {
- }
-
- static bool StartsWith(const std::string& str, const std::string& prefix) {
- return str.compare(0, prefix.size(), prefix) == 0;
- }
-
- static bool EndsWith(const std::string& str, const std::string& suffix) {
- if (suffix.size() > str.size()) {
- return false;
- }
-
- return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
- }
-
- // Returns true iff. a given path is whitelisted. A path is whitelisted
- // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
- // under /system/framework that ends with ".jar" or if it is a system
- // framework overlay.
- static bool IsWhitelisted(const std::string& path) {
- for (size_t i = 0; i < (sizeof(kPathWhitelist) / sizeof(kPathWhitelist[0])); ++i) {
- if (kPathWhitelist[i] == path) {
- return true;
- }
- }
-
- static const std::string kFrameworksPrefix = "/system/framework/";
- static const std::string kJarSuffix = ".jar";
- if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
- return true;
- }
-
- // Whitelist files needed for Runtime Resource Overlay, like these:
- // /system/vendor/overlay/framework-res.apk
- // /system/vendor/overlay-subdir/pg/framework-res.apk
- // /vendor/overlay/framework-res.apk
- // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
- // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
- // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
- // See AssetManager.cpp for more details on overlay-subdir.
- static const std::string kOverlayDir = "/system/vendor/overlay/";
- static const std::string kVendorOverlayDir = "/vendor/overlay";
- static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
- static const std::string kApkSuffix = ".apk";
-
- if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
- || StartsWith(path, kVendorOverlayDir))
- && EndsWith(path, kApkSuffix)
- && path.find("/../") == std::string::npos) {
- return true;
- }
-
- static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
- static const std::string kOverlayIdmapSuffix = ".apk@idmap";
- if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
- && path.find("/../") == std::string::npos) {
- return true;
- }
-
- // All regular files that are placed under this path are whitelisted automatically.
- static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
- if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
- return true;
- }
-
- return false;
- }
-
- // TODO: Call android::base::Readlink instead of copying the code here.
- static bool Readlink(const int fd, std::string* result) {
- char path[64];
- snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
-
- // Code copied from android::base::Readlink starts here :
-
- // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
- // and truncates to whatever size you do supply, so it can't be used to query.
- // We could call lstat first, but that would introduce a race condition that
- // we couldn't detect.
- // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
- char buf[4096];
- ssize_t len = readlink(path, buf, sizeof(buf));
- if (len == -1) return false;
-
- result->assign(buf, len);
- return true;
- }
-
- // Returns the locally-bound name of the socket |fd|. Returns true
- // iff. all of the following hold :
- //
- // - the socket's sa_family is AF_UNIX.
- // - the length of the path is greater than zero (i.e, not an unnamed socket).
- // - the first byte of the path isn't zero (i.e, not a socket with an abstract
- // address).
- static bool GetSocketName(const int fd, std::string* result) {
- sockaddr_storage ss;
- sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
- socklen_t addr_len = sizeof(ss);
-
- if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
- ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
- return false;
- }
-
- if (addr->sa_family != AF_UNIX) {
- ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
- return false;
- }
-
- const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
-
- size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
- // This is an unnamed local socket, we do not accept it.
- if (path_len == 0) {
- ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
- return false;
- }
-
- // This is a local socket with an abstract address, we do not accept it.
- if (unix_addr->sun_path[0] == '\0') {
- ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
- return false;
- }
-
- // If we're here, sun_path must refer to a null terminated filesystem
- // pathname (man 7 unix). Remove the terminator before assigning it to an
- // std::string.
- if (unix_addr->sun_path[path_len - 1] == '\0') {
- --path_len;
- }
-
- result->assign(unix_addr->sun_path, path_len);
- return true;
- }
-
- bool DetachSocket() const {
- const int dev_null_fd = open("/dev/null", O_RDWR);
- if (dev_null_fd < 0) {
- ALOGE("Failed to open /dev/null : %s", strerror(errno));
- return false;
- }
-
- if (dup2(dev_null_fd, fd) == -1) {
- ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
- return false;
- }
-
- if (close(dev_null_fd) == -1) {
- ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
- return false;
- }
-
- return true;
- }
-
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
-};
-
-// A FileDescriptorTable is a collection of FileDescriptorInfo objects
-// keyed by their FDs.
-class FileDescriptorTable {
- public:
- // Creates a new FileDescriptorTable. This function scans
- // /proc/self/fd for the list of open file descriptors and collects
- // information about them. Returns NULL if an error occurs.
- static FileDescriptorTable* Create() {
- DIR* d = opendir(kFdPath);
- if (d == NULL) {
- ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
- return NULL;
- }
- int dir_fd = dirfd(d);
- dirent* e;
-
- std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
- if (fd == -1) {
- continue;
- }
-
- FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
- if (info == NULL) {
- if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
- }
- return NULL;
- }
- open_fd_map[fd] = info;
- }
-
- if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
- return NULL;
- }
- return new FileDescriptorTable(open_fd_map);
- }
-
- bool Restat() {
- std::set<int> open_fds;
-
- // First get the list of open descriptors.
- DIR* d = opendir(kFdPath);
- if (d == NULL) {
- ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
- return false;
- }
-
- int dir_fd = dirfd(d);
- dirent* e;
- while ((e = readdir(d)) != NULL) {
- const int fd = ParseFd(e, dir_fd);
- if (fd == -1) {
- continue;
- }
-
- open_fds.insert(fd);
- }
-
- if (closedir(d) == -1) {
- ALOGE("Unable to close directory : %s", strerror(errno));
- return false;
- }
-
- return RestatInternal(open_fds);
- }
-
- // Reopens all file descriptors that are contained in the table. Returns true
- // if all descriptors were successfully re-opened or detached, and false if an
- // error occurred.
- bool ReopenOrDetach() {
- std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
- for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
- const FileDescriptorInfo* info = it->second;
- if (info == NULL || !info->ReopenOrDetach()) {
- return false;
- }
- }
-
- return true;
- }
-
- private:
- FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map)
- : open_fd_map_(map) {
- }
-
- bool RestatInternal(std::set<int>& open_fds) {
- bool error = false;
-
- // Iterate through the list of file descriptors we've already recorded
- // and check whether :
- //
- // (a) they continue to be open.
- // (b) they refer to the same file.
- std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
- while (it != open_fd_map_.end()) {
- std::set<int>::const_iterator element = open_fds.find(it->first);
- if (element == open_fds.end()) {
- // The entry from the file descriptor table is no longer in the list
- // of open files. We warn about this condition and remove it from
- // the list of FDs under consideration.
- //
- // TODO(narayan): This will be an error in a future android release.
- // error = true;
- // ALOGW("Zygote closed file descriptor %d.", it->first);
- it = open_fd_map_.erase(it);
- } else {
- // The entry from the file descriptor table is still open. Restat
- // it and check whether it refers to the same file.
- const bool same_file = it->second->Restat();
- if (!same_file) {
- // The file descriptor refers to a different description. We must
- // update our entry in the table.
- delete it->second;
- it->second = FileDescriptorInfo::createFromFd(*element);
- if (it->second == NULL) {
- // The descriptor no longer no longer refers to a whitelisted file.
- // We flag an error and remove it from the list of files we're
- // tracking.
- error = true;
- it = open_fd_map_.erase(it);
- } else {
- // Successfully restatted the file, move on to the next open FD.
- ++it;
- }
- } else {
- // It's the same file. Nothing to do here. Move on to the next open
- // FD.
- ++it;
- }
-
- // Finally, remove the FD from the set of open_fds. We do this last because
- // |element| will not remain valid after a call to erase.
- open_fds.erase(element);
- }
- }
-
- if (open_fds.size() > 0) {
- // The zygote has opened new file descriptors since our last inspection.
- // We warn about this condition and add them to our table.
- //
- // TODO(narayan): This will be an error in a future android release.
- // error = true;
- // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
-
- // TODO(narayan): This code will be removed in a future android release.
- std::set<int>::const_iterator it;
- for (it = open_fds.begin(); it != open_fds.end(); ++it) {
- const int fd = (*it);
- FileDescriptorInfo* info = FileDescriptorInfo::createFromFd(fd);
- if (info == NULL) {
- // A newly opened file is not on the whitelist. Flag an error and
- // continue.
- error = true;
- } else {
- // Track the newly opened file.
- open_fd_map_[fd] = info;
- }
- }
- }
-
- return !error;
- }
-
- static int ParseFd(dirent* e, int dir_fd) {
- char* end;
- const int fd = strtol(e->d_name, &end, 10);
- if ((*end) != '\0') {
- return -1;
- }
-
- // Don't bother with the standard input/output/error, they're handled
- // specially post-fork anyway.
- if (fd <= STDERR_FILENO || fd == dir_fd) {
- return -1;
- }
-
- return fd;
- }
-
- // Invariant: All values in this unordered_map are non-NULL.
- std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
-
- DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
-};
diff --git a/core/jni/fd_utils.cpp b/core/jni/fd_utils.cpp
new file mode 100644
index 0000000..969d336f3
--- /dev/null
+++ b/core/jni/fd_utils.cpp
@@ -0,0 +1,559 @@
+/*
+ * 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.
+ */
+
+#include "fd_utils.h"
+
+#include <algorithm>
+
+#include <fcntl.h>
+#include <grp.h>
+#include <stdlib.h>
+#include <sys/socket.h>
+#include <sys/types.h>
+#include <sys/un.h>
+#include <unistd.h>
+
+#include <android-base/strings.h>
+#include <cutils/log.h>
+
+// Static whitelist of open paths that the zygote is allowed to keep open.
+static const char* kPathWhitelist[] = {
+ "/dev/null",
+ "/dev/socket/zygote",
+ "/dev/socket/zygote_secondary",
+ "/dev/socket/webview_zygote",
+ "/sys/kernel/debug/tracing/trace_marker",
+ "/system/framework/framework-res.apk",
+ "/dev/urandom",
+ "/dev/ion",
+ "/dev/dri/renderD129", // Fixes b/31172436
+};
+
+static const char kFdPath[] = "/proc/self/fd";
+
+// static
+FileDescriptorWhitelist* FileDescriptorWhitelist::Get() {
+ if (instance_ == nullptr) {
+ instance_ = new FileDescriptorWhitelist();
+ }
+ return instance_;
+}
+
+bool FileDescriptorWhitelist::IsAllowed(const std::string& path) const {
+ // Check the static whitelist path.
+ for (const auto& whitelist_path : kPathWhitelist) {
+ if (path == whitelist_path)
+ return true;
+ }
+
+ // Check any paths added to the dynamic whitelist.
+ for (const auto& whitelist_path : whitelist_) {
+ if (path == whitelist_path)
+ return true;
+ }
+
+ static const std::string kFrameworksPrefix = "/system/framework/";
+ static const std::string kJarSuffix = ".jar";
+ if (StartsWith(path, kFrameworksPrefix) && EndsWith(path, kJarSuffix)) {
+ return true;
+ }
+
+ // Whitelist files needed for Runtime Resource Overlay, like these:
+ // /system/vendor/overlay/framework-res.apk
+ // /system/vendor/overlay-subdir/pg/framework-res.apk
+ // /vendor/overlay/framework-res.apk
+ // /vendor/overlay/PG/android-framework-runtime-resource-overlay.apk
+ // /data/resource-cache/system@vendor@overlay@framework-res.apk@idmap
+ // /data/resource-cache/system@vendor@overlay-subdir@pg@framework-res.apk@idmap
+ // See AssetManager.cpp for more details on overlay-subdir.
+ static const std::string kOverlayDir = "/system/vendor/overlay/";
+ static const std::string kVendorOverlayDir = "/vendor/overlay";
+ static const std::string kOverlaySubdir = "/system/vendor/overlay-subdir/";
+ static const std::string kApkSuffix = ".apk";
+
+ if ((StartsWith(path, kOverlayDir) || StartsWith(path, kOverlaySubdir)
+ || StartsWith(path, kVendorOverlayDir))
+ && EndsWith(path, kApkSuffix)
+ && path.find("/../") == std::string::npos) {
+ return true;
+ }
+
+ static const std::string kOverlayIdmapPrefix = "/data/resource-cache/";
+ static const std::string kOverlayIdmapSuffix = ".apk@idmap";
+ if (StartsWith(path, kOverlayIdmapPrefix) && EndsWith(path, kOverlayIdmapSuffix)
+ && path.find("/../") == std::string::npos) {
+ return true;
+ }
+
+ // All regular files that are placed under this path are whitelisted automatically.
+ static const std::string kZygoteWhitelistPath = "/vendor/zygote_whitelist/";
+ if (StartsWith(path, kZygoteWhitelistPath) && path.find("/../") == std::string::npos) {
+ return true;
+ }
+
+ return false;
+}
+
+FileDescriptorWhitelist::FileDescriptorWhitelist()
+ : whitelist_() {
+}
+
+// TODO: Call android::base::StartsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::StartsWith(const std::string& str,
+ const std::string& prefix) {
+ return str.compare(0, prefix.size(), prefix) == 0;
+}
+
+// TODO: Call android::base::EndsWith instead of copying the code here.
+// static
+bool FileDescriptorWhitelist::EndsWith(const std::string& str,
+ const std::string& suffix) {
+ if (suffix.size() > str.size()) {
+ return false;
+ }
+
+ return str.compare(str.size() - suffix.size(), suffix.size(), suffix) == 0;
+}
+
+FileDescriptorWhitelist* FileDescriptorWhitelist::instance_ = nullptr;
+
+// static
+FileDescriptorInfo* FileDescriptorInfo::CreateFromFd(int fd) {
+ struct stat f_stat;
+ // This should never happen; the zygote should always have the right set
+ // of permissions required to stat all its open files.
+ if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+ ALOGE("Unable to stat fd %d : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ const FileDescriptorWhitelist* whitelist = FileDescriptorWhitelist::Get();
+
+ if (S_ISSOCK(f_stat.st_mode)) {
+ std::string socket_name;
+ if (!GetSocketName(fd, &socket_name)) {
+ return NULL;
+ }
+
+ if (!whitelist->IsAllowed(socket_name)) {
+ ALOGE("Socket name not whitelisted : %s (fd=%d)", socket_name.c_str(), fd);
+ return NULL;
+ }
+
+ return new FileDescriptorInfo(fd);
+ }
+
+ // We only handle whitelisted regular files and character devices. Whitelisted
+ // character devices must provide a guarantee of sensible behaviour when
+ // reopened.
+ //
+ // S_ISDIR : Not supported. (We could if we wanted to, but it's unused).
+ // S_ISLINK : Not supported.
+ // S_ISBLK : Not supported.
+ // S_ISFIFO : Not supported. Note that the zygote uses pipes to communicate
+ // with the child process across forks but those should have been closed
+ // before we got to this point.
+ if (!S_ISCHR(f_stat.st_mode) && !S_ISREG(f_stat.st_mode)) {
+ ALOGE("Unsupported st_mode %d", f_stat.st_mode);
+ return NULL;
+ }
+
+ std::string file_path;
+ if (!Readlink(fd, &file_path)) {
+ return NULL;
+ }
+
+ if (!whitelist->IsAllowed(file_path)) {
+ ALOGE("Not whitelisted : %s", file_path.c_str());
+ return NULL;
+ }
+
+ // File descriptor flags : currently on FD_CLOEXEC. We can set these
+ // using F_SETFD - we're single threaded at this point of execution so
+ // there won't be any races.
+ const int fd_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFD));
+ if (fd_flags == -1) {
+ ALOGE("Failed fcntl(%d, F_GETFD) : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ // File status flags :
+ // - File access mode : (O_RDONLY, O_WRONLY...) we'll pass these through
+ // to the open() call.
+ //
+ // - File creation flags : (O_CREAT, O_EXCL...) - there's not much we can
+ // do about these, since the file has already been created. We shall ignore
+ // them here.
+ //
+ // - Other flags : We'll have to set these via F_SETFL. On linux, F_SETFL
+ // can only set O_APPEND, O_ASYNC, O_DIRECT, O_NOATIME, and O_NONBLOCK.
+ // In particular, it can't set O_SYNC and O_DSYNC. We'll have to test for
+ // their presence and pass them in to open().
+ int fs_flags = TEMP_FAILURE_RETRY(fcntl(fd, F_GETFL));
+ if (fs_flags == -1) {
+ ALOGE("Failed fcntl(%d, F_GETFL) : %s", fd, strerror(errno));
+ return NULL;
+ }
+
+ // File offset : Ignore the offset for non seekable files.
+ const off_t offset = TEMP_FAILURE_RETRY(lseek64(fd, 0, SEEK_CUR));
+
+ // We pass the flags that open accepts to open, and use F_SETFL for
+ // the rest of them.
+ static const int kOpenFlags = (O_RDONLY | O_WRONLY | O_RDWR | O_DSYNC | O_SYNC);
+ int open_flags = fs_flags & (kOpenFlags);
+ fs_flags = fs_flags & (~(kOpenFlags));
+
+ return new FileDescriptorInfo(f_stat, file_path, fd, open_flags, fd_flags, fs_flags, offset);
+}
+
+bool FileDescriptorInfo::Restat() const {
+ struct stat f_stat;
+ if (TEMP_FAILURE_RETRY(fstat(fd, &f_stat)) == -1) {
+ return false;
+ }
+
+ return f_stat.st_ino == stat.st_ino && f_stat.st_dev == stat.st_dev;
+}
+
+bool FileDescriptorInfo::ReopenOrDetach() const {
+ if (is_sock) {
+ return DetachSocket();
+ }
+
+ // NOTE: This might happen if the file was unlinked after being opened.
+ // It's a common pattern in the case of temporary files and the like but
+ // we should not allow such usage from the zygote.
+ const int new_fd = TEMP_FAILURE_RETRY(open(file_path.c_str(), open_flags));
+
+ if (new_fd == -1) {
+ ALOGE("Failed open(%s, %d) : %s", file_path.c_str(), open_flags, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFD, fd_flags)) == -1) {
+ close(new_fd);
+ ALOGE("Failed fcntl(%d, F_SETFD, %x) : %s", new_fd, fd_flags, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(fcntl(new_fd, F_SETFL, fs_flags)) == -1) {
+ close(new_fd);
+ ALOGE("Failed fcntl(%d, F_SETFL, %x) : %s", new_fd, fs_flags, strerror(errno));
+ return false;
+ }
+
+ if (offset != -1 && TEMP_FAILURE_RETRY(lseek64(new_fd, offset, SEEK_SET)) == -1) {
+ close(new_fd);
+ ALOGE("Failed lseek64(%d, SEEK_SET) : %s", new_fd, strerror(errno));
+ return false;
+ }
+
+ if (TEMP_FAILURE_RETRY(dup2(new_fd, fd)) == -1) {
+ close(new_fd);
+ ALOGE("Failed dup2(%d, %d) : %s", fd, new_fd, strerror(errno));
+ return false;
+ }
+
+ close(new_fd);
+
+ return true;
+}
+
+FileDescriptorInfo::FileDescriptorInfo(int fd) :
+ fd(fd),
+ stat(),
+ open_flags(0),
+ fd_flags(0),
+ fs_flags(0),
+ offset(0),
+ is_sock(true) {
+}
+
+FileDescriptorInfo::FileDescriptorInfo(struct stat stat, const std::string& file_path,
+ int fd, int open_flags, int fd_flags, int fs_flags,
+ off_t offset) :
+ fd(fd),
+ stat(stat),
+ file_path(file_path),
+ open_flags(open_flags),
+ fd_flags(fd_flags),
+ fs_flags(fs_flags),
+ offset(offset),
+ is_sock(false) {
+}
+
+// TODO: Call android::base::Readlink instead of copying the code here.
+// static
+bool FileDescriptorInfo::Readlink(const int fd, std::string* result) {
+ char path[64];
+ snprintf(path, sizeof(path), "/proc/self/fd/%d", fd);
+
+ // Code copied from android::base::Readlink starts here :
+
+ // Annoyingly, the readlink system call returns EINVAL for a zero-sized buffer,
+ // and truncates to whatever size you do supply, so it can't be used to query.
+ // We could call lstat first, but that would introduce a race condition that
+ // we couldn't detect.
+ // ext2 and ext4 both have PAGE_SIZE limitations, so we assume that here.
+ char buf[4096];
+ ssize_t len = readlink(path, buf, sizeof(buf));
+ if (len == -1) return false;
+
+ result->assign(buf, len);
+ return true;
+}
+
+// static
+bool FileDescriptorInfo::GetSocketName(const int fd, std::string* result) {
+ sockaddr_storage ss;
+ sockaddr* addr = reinterpret_cast<sockaddr*>(&ss);
+ socklen_t addr_len = sizeof(ss);
+
+ if (TEMP_FAILURE_RETRY(getsockname(fd, addr, &addr_len)) == -1) {
+ ALOGE("Failed getsockname(%d) : %s", fd, strerror(errno));
+ return false;
+ }
+
+ if (addr->sa_family != AF_UNIX) {
+ ALOGE("Unsupported socket (fd=%d) with family %d", fd, addr->sa_family);
+ return false;
+ }
+
+ const sockaddr_un* unix_addr = reinterpret_cast<const sockaddr_un*>(&ss);
+
+ size_t path_len = addr_len - offsetof(struct sockaddr_un, sun_path);
+ // This is an unnamed local socket, we do not accept it.
+ if (path_len == 0) {
+ ALOGE("Unsupported AF_UNIX socket (fd=%d) with empty path.", fd);
+ return false;
+ }
+
+ // This is a local socket with an abstract address, we do not accept it.
+ if (unix_addr->sun_path[0] == '\0') {
+ ALOGE("Unsupported AF_UNIX socket (fd=%d) with abstract address.", fd);
+ return false;
+ }
+
+ // If we're here, sun_path must refer to a null terminated filesystem
+ // pathname (man 7 unix). Remove the terminator before assigning it to an
+ // std::string.
+ if (unix_addr->sun_path[path_len - 1] == '\0') {
+ --path_len;
+ }
+
+ result->assign(unix_addr->sun_path, path_len);
+ return true;
+}
+
+bool FileDescriptorInfo::DetachSocket() const {
+ const int dev_null_fd = open("/dev/null", O_RDWR);
+ if (dev_null_fd < 0) {
+ ALOGE("Failed to open /dev/null : %s", strerror(errno));
+ return false;
+ }
+
+ if (dup2(dev_null_fd, fd) == -1) {
+ ALOGE("Failed dup2 on socket descriptor %d : %s", fd, strerror(errno));
+ return false;
+ }
+
+ if (close(dev_null_fd) == -1) {
+ ALOGE("Failed close(%d) : %s", dev_null_fd, strerror(errno));
+ return false;
+ }
+
+ return true;
+}
+
+// static
+FileDescriptorTable* FileDescriptorTable::Create() {
+ DIR* d = opendir(kFdPath);
+ if (d == NULL) {
+ ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ return NULL;
+ }
+ int dir_fd = dirfd(d);
+ dirent* e;
+
+ std::unordered_map<int, FileDescriptorInfo*> open_fd_map;
+ while ((e = readdir(d)) != NULL) {
+ const int fd = ParseFd(e, dir_fd);
+ if (fd == -1) {
+ continue;
+ }
+
+ FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+ if (info == NULL) {
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ }
+ return NULL;
+ }
+ open_fd_map[fd] = info;
+ }
+
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ return NULL;
+ }
+ return new FileDescriptorTable(open_fd_map);
+}
+
+bool FileDescriptorTable::Restat() {
+ std::set<int> open_fds;
+
+ // First get the list of open descriptors.
+ DIR* d = opendir(kFdPath);
+ if (d == NULL) {
+ ALOGE("Unable to open directory %s: %s", kFdPath, strerror(errno));
+ return false;
+ }
+
+ int dir_fd = dirfd(d);
+ dirent* e;
+ while ((e = readdir(d)) != NULL) {
+ const int fd = ParseFd(e, dir_fd);
+ if (fd == -1) {
+ continue;
+ }
+
+ open_fds.insert(fd);
+ }
+
+ if (closedir(d) == -1) {
+ ALOGE("Unable to close directory : %s", strerror(errno));
+ return false;
+ }
+
+ return RestatInternal(open_fds);
+}
+
+// Reopens all file descriptors that are contained in the table. Returns true
+// if all descriptors were successfully re-opened or detached, and false if an
+// error occurred.
+bool FileDescriptorTable::ReopenOrDetach() {
+ std::unordered_map<int, FileDescriptorInfo*>::const_iterator it;
+ for (it = open_fd_map_.begin(); it != open_fd_map_.end(); ++it) {
+ const FileDescriptorInfo* info = it->second;
+ if (info == NULL || !info->ReopenOrDetach()) {
+ return false;
+ }
+ }
+
+ return true;
+}
+
+FileDescriptorTable::FileDescriptorTable(
+ const std::unordered_map<int, FileDescriptorInfo*>& map)
+ : open_fd_map_(map) {
+}
+
+bool FileDescriptorTable::RestatInternal(std::set<int>& open_fds) {
+ bool error = false;
+
+ // Iterate through the list of file descriptors we've already recorded
+ // and check whether :
+ //
+ // (a) they continue to be open.
+ // (b) they refer to the same file.
+ std::unordered_map<int, FileDescriptorInfo*>::iterator it = open_fd_map_.begin();
+ while (it != open_fd_map_.end()) {
+ std::set<int>::const_iterator element = open_fds.find(it->first);
+ if (element == open_fds.end()) {
+ // The entry from the file descriptor table is no longer in the list
+ // of open files. We warn about this condition and remove it from
+ // the list of FDs under consideration.
+ //
+ // TODO(narayan): This will be an error in a future android release.
+ // error = true;
+ // ALOGW("Zygote closed file descriptor %d.", it->first);
+ it = open_fd_map_.erase(it);
+ } else {
+ // The entry from the file descriptor table is still open. Restat
+ // it and check whether it refers to the same file.
+ const bool same_file = it->second->Restat();
+ if (!same_file) {
+ // The file descriptor refers to a different description. We must
+ // update our entry in the table.
+ delete it->second;
+ it->second = FileDescriptorInfo::CreateFromFd(*element);
+ if (it->second == NULL) {
+ // The descriptor no longer no longer refers to a whitelisted file.
+ // We flag an error and remove it from the list of files we're
+ // tracking.
+ error = true;
+ it = open_fd_map_.erase(it);
+ } else {
+ // Successfully restatted the file, move on to the next open FD.
+ ++it;
+ }
+ } else {
+ // It's the same file. Nothing to do here. Move on to the next open
+ // FD.
+ ++it;
+ }
+
+ // Finally, remove the FD from the set of open_fds. We do this last because
+ // |element| will not remain valid after a call to erase.
+ open_fds.erase(element);
+ }
+ }
+
+ if (open_fds.size() > 0) {
+ // The zygote has opened new file descriptors since our last inspection.
+ // We warn about this condition and add them to our table.
+ //
+ // TODO(narayan): This will be an error in a future android release.
+ // error = true;
+ // ALOGW("Zygote opened %zd new file descriptor(s).", open_fds.size());
+
+ // TODO(narayan): This code will be removed in a future android release.
+ std::set<int>::const_iterator it;
+ for (it = open_fds.begin(); it != open_fds.end(); ++it) {
+ const int fd = (*it);
+ FileDescriptorInfo* info = FileDescriptorInfo::CreateFromFd(fd);
+ if (info == NULL) {
+ // A newly opened file is not on the whitelist. Flag an error and
+ // continue.
+ error = true;
+ } else {
+ // Track the newly opened file.
+ open_fd_map_[fd] = info;
+ }
+ }
+ }
+
+ return !error;
+}
+
+// static
+int FileDescriptorTable::ParseFd(dirent* e, int dir_fd) {
+ char* end;
+ const int fd = strtol(e->d_name, &end, 10);
+ if ((*end) != '\0') {
+ return -1;
+ }
+
+ // Don't bother with the standard input/output/error, they're handled
+ // specially post-fork anyway.
+ if (fd <= STDERR_FILENO || fd == dir_fd) {
+ return -1;
+ }
+
+ return fd;
+}
diff --git a/core/jni/fd_utils.h b/core/jni/fd_utils.h
new file mode 100644
index 0000000..9e3afd9
--- /dev/null
+++ b/core/jni/fd_utils.h
@@ -0,0 +1,147 @@
+/*
+ * 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.
+ */
+
+#ifndef FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+#define FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
+
+#include <set>
+#include <string>
+#include <unordered_map>
+#include <vector>
+
+#include <dirent.h>
+#include <inttypes.h>
+#include <sys/stat.h>
+
+#include <android-base/macros.h>
+
+// Whitelist of open paths that the zygote is allowed to keep open.
+//
+// In addition to the paths listed in kPathWhitelist in file_utils.cpp, and
+// paths dynamically added with Allow(), all files ending with ".jar"
+// under /system/framework" are whitelisted. See IsAllowed() for the canonical
+// definition.
+//
+// If the whitelisted path is associated with a regular file or a
+// character device, the file is reopened after a fork with the same
+// offset and mode. If the whilelisted path is associated with a
+// AF_UNIX socket, the socket will refer to /dev/null after each
+// fork, and all operations on it will fail.
+class FileDescriptorWhitelist {
+ public:
+ // Lazily creates the global whitelist.
+ static FileDescriptorWhitelist* Get();
+
+ // Adds a path to the whitelist.
+ void Allow(const std::string& path) {
+ whitelist_.push_back(path);
+ }
+
+ // Returns true iff. a given path is whitelisted. A path is whitelisted
+ // if it belongs to the whitelist (see kPathWhitelist) or if it's a path
+ // under /system/framework that ends with ".jar" or if it is a system
+ // framework overlay.
+ bool IsAllowed(const std::string& path) const;
+
+ private:
+ FileDescriptorWhitelist();
+
+ static bool StartsWith(const std::string& str, const std::string& prefix);
+
+ static bool EndsWith(const std::string& str, const std::string& suffix);
+
+ static FileDescriptorWhitelist* instance_;
+
+ std::vector<std::string> whitelist_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorWhitelist);
+};
+
+// Keeps track of all relevant information (flags, offset etc.) of an
+// open zygote file descriptor.
+class FileDescriptorInfo {
+ public:
+ // Create a FileDescriptorInfo for a given file descriptor. Returns
+ // |NULL| if an error occurred.
+ static FileDescriptorInfo* CreateFromFd(int fd);
+
+ // Checks whether the file descriptor associated with this object
+ // refers to the same description.
+ bool Restat() const;
+
+ bool ReopenOrDetach() const;
+
+ const int fd;
+ const struct stat stat;
+ const std::string file_path;
+ const int open_flags;
+ const int fd_flags;
+ const int fs_flags;
+ const off_t offset;
+ const bool is_sock;
+
+ private:
+ FileDescriptorInfo(int fd);
+
+ FileDescriptorInfo(struct stat stat, const std::string& file_path, int fd, int open_flags,
+ int fd_flags, int fs_flags, off_t offset);
+
+ static bool Readlink(const int fd, std::string* result);
+
+ // Returns the locally-bound name of the socket |fd|. Returns true
+ // iff. all of the following hold :
+ //
+ // - the socket's sa_family is AF_UNIX.
+ // - the length of the path is greater than zero (i.e, not an unnamed socket).
+ // - the first byte of the path isn't zero (i.e, not a socket with an abstract
+ // address).
+ static bool GetSocketName(const int fd, std::string* result);
+
+ bool DetachSocket() const;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorInfo);
+};
+
+// A FileDescriptorTable is a collection of FileDescriptorInfo objects
+// keyed by their FDs.
+class FileDescriptorTable {
+ public:
+ // Creates a new FileDescriptorTable. This function scans
+ // /proc/self/fd for the list of open file descriptors and collects
+ // information about them. Returns NULL if an error occurs.
+ static FileDescriptorTable* Create();
+
+ bool Restat();
+
+ // Reopens all file descriptors that are contained in the table. Returns true
+ // if all descriptors were successfully re-opened or detached, and false if an
+ // error occurred.
+ bool ReopenOrDetach();
+
+ private:
+ FileDescriptorTable(const std::unordered_map<int, FileDescriptorInfo*>& map);
+
+ bool RestatInternal(std::set<int>& open_fds);
+
+ static int ParseFd(dirent* e, int dir_fd);
+
+ // Invariant: All values in this unordered_map are non-NULL.
+ std::unordered_map<int, FileDescriptorInfo*> open_fd_map_;
+
+ DISALLOW_COPY_AND_ASSIGN(FileDescriptorTable);
+};
+
+#endif // FRAMEWORKS_BASE_CORE_JNI_FD_UTILS_H_
diff --git a/core/jni/include/android_runtime/android_view_Surface.h b/core/jni/include/android_runtime/android_view_Surface.h
index b1e552a..1bc8521 100644
--- a/core/jni/include/android_runtime/android_view_Surface.h
+++ b/core/jni/include/android_runtime/android_view_Surface.h
@@ -42,6 +42,7 @@
NV16 = 0x10,
NV21 = 0x11,
YUY2 = 0x14,
+ RGBA_FP16 = 0x16,
RAW_SENSOR = 0x20,
PRIVATE = 0x22,
YUV_420_888 = 0x23,
diff --git a/core/tests/coretests/src/android/view/ViewCaptureTest.java b/core/tests/coretests/src/android/view/ViewCaptureTest.java
index 15cfe23..1b4d4a2 100644
--- a/core/tests/coretests/src/android/view/ViewCaptureTest.java
+++ b/core/tests/coretests/src/android/view/ViewCaptureTest.java
@@ -16,6 +16,8 @@
package android.view;
+import static org.junit.Assert.assertTrue;
+
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
@@ -32,8 +34,6 @@
import org.junit.Test;
import org.junit.runner.RunWith;
-import static org.junit.Assert.assertTrue;
-
@RunWith(AndroidJUnit4.class)
public class ViewCaptureTest {
@@ -70,6 +70,7 @@
private void testCreateSnapshot(boolean skipChildren, int goldenResId) {
Bitmap result = mViewToCapture.createSnapshot(Bitmap.Config.ARGB_8888, 0, skipChildren);
+ result.setHasAlpha(false); // resource will have no alpha, since content is opaque
Bitmap golden = BitmapFactory.decodeResource(mActivity.getResources(), goldenResId);
assertTrue(golden.sameAs(result));
}
diff --git a/graphics/java/android/graphics/PixelFormat.java b/graphics/java/android/graphics/PixelFormat.java
index 0fa52f8..c3a9443 100644
--- a/graphics/java/android/graphics/PixelFormat.java
+++ b/graphics/java/android/graphics/PixelFormat.java
@@ -29,7 +29,7 @@
/** @hide */
@Retention(RetentionPolicy.SOURCE)
- @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGBX_F16, RGB_888, RGB_565})
+ @IntDef({RGBA_8888, RGBX_8888, RGBA_F16, RGB_888, RGB_565})
public @interface Format { };
// NOTE: these constants must match the values from graphics/common/x.x/types.hal
@@ -88,7 +88,6 @@
public static final int YCbCr_422_I = 0x14;
public static final int RGBA_F16 = 0x16;
- public static final int RGBX_F16 = 0x17;
/**
* @deprecated use {@link android.graphics.ImageFormat#JPEG
@@ -134,7 +133,6 @@
info.bytesPerPixel = 1;
break;
case RGBA_F16:
- case RGBX_F16:
info.bitsPerPixel = 64;
info.bytesPerPixel = 8;
break;
@@ -178,7 +176,6 @@
case RGB_888:
case RGB_565:
case RGBA_F16:
- case RGBX_F16:
return true;
}
diff --git a/packages/SystemUI/res/drawable/header_dot.xml b/packages/SystemUI/res/drawable/header_dot.xml
index 568a9c2..dbc6b37 100644
--- a/packages/SystemUI/res/drawable/header_dot.xml
+++ b/packages/SystemUI/res/drawable/header_dot.xml
@@ -2,26 +2,27 @@
<!--
** Copyright 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
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
**
-** http://www.apache.org/licenses/LICENSE-2.0
+** http://www.apache.org/licenses/LICENSE-2.0
**
-** Unless required by applicable law or agreed to in writing, software
-** distributed under the License is distributed on an "AS IS" BASIS,
-** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-** See the License for the specific language governing permissions and
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
** limitations under the License.
-->
<shape
xmlns:android="http://schemas.android.com/apk/res/android"
- android:shape="oval">
+ android:shape="oval"
+ android:tint="?android:attr/colorControlNormal">
- <solid
+ <solid
android:color="#FFFFFF"/>
- <size
+ <size
android:width="3dp"
android:height="3dp"/>
</shape>
diff --git a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
index 13e047c..6a868d5 100644
--- a/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
+++ b/packages/SystemUI/src/com/android/systemui/doze/DozeMachine.java
@@ -218,7 +218,8 @@
Preconditions.checkState(mState == State.DOZE_REQUEST_PULSE);
break;
case DOZE_PULSE_DONE:
- Preconditions.checkState(mState == State.DOZE_PULSING);
+ Preconditions.checkState(
+ mState == State.DOZE_REQUEST_PULSE || mState == State.DOZE_PULSING);
break;
default:
break;
diff --git a/packages/SystemUI/src/com/android/systemui/recents/Recents.java b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
index 790f3f6..d9fc2fc 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/Recents.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/Recents.java
@@ -207,8 +207,6 @@
getSystemService(Context.UI_MODE_SERVICE);
if (uiModeManager.getCurrentModeType() == Configuration.UI_MODE_TYPE_TELEVISION) {
mImpl = new RecentsTvImpl(mContext);
- } else if (SystemProperties.getBoolean("ro.recents.grid", false) == true) {
- mImpl = new RecentsGridImpl(mContext);
} else {
mImpl = new RecentsImpl(mContext);
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
index 8b99d72..c3948258 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/doze/DozeMachineTest.java
@@ -214,6 +214,16 @@
@Test
@UiThreadTest
+ public void testSuppressingPulse_doesntCrash() {
+ mMachine.requestState(INITIALIZED);
+
+ mMachine.requestState(DOZE);
+ mMachine.requestState(DOZE_REQUEST_PULSE);
+ mMachine.requestState(DOZE_PULSE_DONE);
+ }
+
+ @Test
+ @UiThreadTest
public void testScreen_offInDoze() {
mMachine.requestState(INITIALIZED);
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index 9b67bd2..eae4905 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -369,6 +369,7 @@
import static com.android.server.am.ActivityStackSupervisor.FORCE_FOCUS;
import static com.android.server.am.ActivityStackSupervisor.ON_TOP;
import static com.android.server.am.ActivityStackSupervisor.PRESERVE_WINDOWS;
+import static com.android.server.am.ActivityStackSupervisor.REMOVE_FROM_RECENTS;
import static com.android.server.am.ActivityStackSupervisor.RESTORE_FROM_RECENTS;
import static com.android.server.am.TaskRecord.INVALID_TASK_ID;
import static com.android.server.am.TaskRecord.LOCK_TASK_AUTH_DONT_LOCK;
@@ -539,8 +540,6 @@
private static final String INTENT_REMOTE_BUGREPORT_FINISHED =
"android.intent.action.REMOTE_BUGREPORT_FINISHED";
- // Used to indicate that a task is removed it should also be removed from recents.
- private static final boolean REMOVE_FROM_RECENTS = true;
// Used to indicate that an app transition should be animated.
static final boolean ANIMATE = true;
@@ -4826,7 +4825,7 @@
// because we don't support returning them across task boundaries. Also, to
// keep backwards compatibility we remove the task from recents when finishing
// task with root activity.
- res = removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
+ res = mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, finishWithRootActivity);
if (!res) {
Slog.i(TAG, "Removing task failed to finish activity");
}
@@ -5471,7 +5470,7 @@
tr.getBaseIntent().getComponent().getPackageName();
if (tr.userId != userId) continue;
if (!taskPackageName.equals(packageName)) continue;
- removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
@@ -9585,79 +9584,6 @@
mWindowManager.executeAppTransition();
}
- private void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess,
- boolean removeFromRecents) {
- if (removeFromRecents) {
- mRecentTasks.remove(tr);
- tr.removedFromRecents();
- }
- ComponentName component = tr.getBaseIntent().getComponent();
- if (component == null) {
- Slog.w(TAG, "No component for base intent of task: " + tr);
- return;
- }
-
- // Find any running services associated with this app and stop if needed.
- mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
-
- if (!killProcess) {
- return;
- }
-
- // Determine if the process(es) for this task should be killed.
- final String pkg = component.getPackageName();
- ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
- ArrayMap<String, SparseArray<ProcessRecord>> pmap = mProcessNames.getMap();
- for (int i = 0; i < pmap.size(); i++) {
-
- SparseArray<ProcessRecord> uids = pmap.valueAt(i);
- for (int j = 0; j < uids.size(); j++) {
- ProcessRecord proc = uids.valueAt(j);
- if (proc.userId != tr.userId) {
- // Don't kill process for a different user.
- continue;
- }
- if (proc == mHomeProcess) {
- // Don't kill the home process along with tasks from the same package.
- continue;
- }
- if (!proc.pkgList.containsKey(pkg)) {
- // Don't kill process that is not associated with this task.
- continue;
- }
-
- for (int k = 0; k < proc.activities.size(); k++) {
- TaskRecord otherTask = proc.activities.get(k).task;
- if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
- // Don't kill process(es) that has an activity in a different task that is
- // also in recents.
- return;
- }
- }
-
- if (proc.foregroundServices) {
- // Don't kill process(es) with foreground service.
- return;
- }
-
- // Add process to kill list.
- procsToKill.add(proc);
- }
- }
-
- // Kill the running processes.
- for (int i = 0; i < procsToKill.size(); i++) {
- ProcessRecord pr = procsToKill.get(i);
- if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
- && pr.curReceivers.isEmpty()) {
- pr.kill("remove task", true);
- } else {
- // We delay killing processes that are not in the background or running a receiver.
- pr.waitingToKill = "remove task";
- }
- }
- }
-
private void removeTasksByPackageNameLocked(String packageName, int userId) {
// Remove all tasks with activities in the specified package from the list of recent tasks
for (int i = mRecentTasks.size() - 1; i >= 0; i--) {
@@ -9667,7 +9593,7 @@
ComponentName cn = tr.intent.getComponent();
if (cn != null && cn.getPackageName().equals(packageName)) {
// If the package name matches, remove the task.
- removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, true, REMOVE_FROM_RECENTS);
}
}
}
@@ -9685,35 +9611,11 @@
final boolean sameComponent = cn != null && cn.getPackageName().equals(packageName)
&& (filterByClasses == null || filterByClasses.contains(cn.getClassName()));
if (sameComponent) {
- removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
+ mStackSupervisor.removeTaskByIdLocked(tr.taskId, false, REMOVE_FROM_RECENTS);
}
}
}
- /**
- * Removes the task with the specified task id.
- *
- * @param taskId Identifier of the task to be removed.
- * @param killProcess Kill any process associated with the task if possible.
- * @param removeFromRecents Whether to also remove the task from recents.
- * @return Returns true if the given task was found and removed.
- */
- private boolean removeTaskByIdLocked(int taskId, boolean killProcess,
- boolean removeFromRecents) {
- final TaskRecord tr = mStackSupervisor.anyTaskForIdLocked(
- taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
- if (tr != null) {
- tr.removeTaskActivitiesLocked();
- cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
- if (tr.isPersistable) {
- notifyTaskPersisterLocked(null, true);
- }
- return true;
- }
- Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
- return false;
- }
-
@Override
public void removeStack(int stackId) {
enforceCallingPermission(Manifest.permission.MANAGE_ACTIVITY_STACKS, "removeStack()");
@@ -9724,15 +9626,7 @@
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- final ActivityStack stack = mStackSupervisor.getStack(stackId);
- if (stack == null) {
- return;
- }
- final ArrayList<TaskRecord> tasks = stack.getAllTasks();
- for (int i = tasks.size() - 1; i >= 0; i--) {
- removeTaskByIdLocked(
- tasks.get(i).taskId, true /* killProcess */, REMOVE_FROM_RECENTS);
- }
+ mStackSupervisor.removeStackLocked(stackId);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -9761,7 +9655,7 @@
synchronized (this) {
final long ident = Binder.clearCallingIdentity();
try {
- return removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
+ return mStackSupervisor.removeTaskByIdLocked(taskId, true, REMOVE_FROM_RECENTS);
} finally {
Binder.restoreCallingIdentity(ident);
}
@@ -22520,7 +22414,8 @@
long origId = Binder.clearCallingIdentity();
try {
// We remove the task from recents to preserve backwards
- if (!removeTaskByIdLocked(mTaskId, false, REMOVE_FROM_RECENTS)) {
+ if (!mStackSupervisor.removeTaskByIdLocked(mTaskId, false,
+ REMOVE_FROM_RECENTS)) {
throw new IllegalArgumentException("Unable to find task ID " + mTaskId);
}
} finally {
diff --git a/services/core/java/com/android/server/am/ActivityStackSupervisor.java b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
index fe261f1..b4b3465 100644
--- a/services/core/java/com/android/server/am/ActivityStackSupervisor.java
+++ b/services/core/java/com/android/server/am/ActivityStackSupervisor.java
@@ -249,6 +249,9 @@
// Don't execute any calls to resume.
static final boolean DEFER_RESUME = true;
+ // Used to indicate that a task is removed it should also be removed from recents.
+ static final boolean REMOVE_FROM_RECENTS = true;
+
// Activity actions an app cannot start if it uses a permission which is not granted.
private static final ArrayMap<String, String> ACTION_TO_RUNTIME_PERMISSION =
new ArrayMap<>();
@@ -2183,7 +2186,8 @@
// Update the return-to to reflect where the pinned stack task was moved
// from so that we retain the stack that was previously visible if the
// pinned stack is recreated. See moveActivityToPinnedStackLocked().
- task.setTaskToReturnTo(getFocusedStack().getStackId() == HOME_STACK_ID
+ final int focusedStackId = getFocusedStack().getStackId();
+ task.setTaskToReturnTo(focusedStackId == HOME_STACK_ID || !onTop
? HOME_ACTIVITY_TYPE : APPLICATION_ACTIVITY_TYPE);
}
moveTaskToStackLocked(tasks.get(i).taskId,
@@ -2374,6 +2378,141 @@
return activityContainer.mStack;
}
+ /**
+ * Removes the stack associed with the given {@param stackId}. If the {@param stackId} is the
+ * pinned stack, then its tasks are not explicitly removed when the stack is destroyed, but
+ * instead moved back onto the fullscreen stack.
+ */
+ void removeStackLocked(int stackId) {
+ final ActivityStack stack = getStack(stackId);
+ if (stack == null) {
+ return;
+ }
+
+ final ArrayList<TaskRecord> tasks = stack.getAllTasks();
+ if (stack.getStackId() == PINNED_STACK_ID) {
+ final ActivityStack fullscreenStack = getStack(FULLSCREEN_WORKSPACE_STACK_ID);
+ if (fullscreenStack != null) {
+ final boolean isFullscreenStackVisible =
+ fullscreenStack.getStackVisibilityLocked(null) == STACK_VISIBLE;
+ for (int i = 0; i < tasks.size(); i++) {
+ // Insert the task either at the top of the fullscreen stack if it is hidden,
+ // or just under the top task if it is currently visible
+ final int insertPosition = isFullscreenStackVisible
+ ? Math.max(0, fullscreenStack.getChildCount() - 1)
+ : fullscreenStack.getChildCount();
+ positionTaskInStackLocked(tasks.get(i).taskId, FULLSCREEN_WORKSPACE_STACK_ID,
+ insertPosition);
+ }
+ ensureActivitiesVisibleLocked(null, 0, !PRESERVE_WINDOWS);
+ resumeFocusedStackTopActivityLocked();
+ } else {
+ // If there is no fullscreen stack, then create the stack and move all the tasks
+ // onto the stack
+ moveTasksToFullscreenStackLocked(PINNED_STACK_ID, false /* onTop */);
+ }
+ } else {
+ for (int i = tasks.size() - 1; i >= 0; i--) {
+ removeTaskByIdLocked(tasks.get(i).taskId, true /* killProcess */,
+ REMOVE_FROM_RECENTS);
+ }
+ }
+ }
+
+ /**
+ * Removes the task with the specified task id.
+ *
+ * @param taskId Identifier of the task to be removed.
+ * @param killProcess Kill any process associated with the task if possible.
+ * @param removeFromRecents Whether to also remove the task from recents.
+ * @return Returns true if the given task was found and removed.
+ */
+ boolean removeTaskByIdLocked(int taskId, boolean killProcess, boolean removeFromRecents) {
+ final TaskRecord tr = anyTaskForIdLocked(taskId, !RESTORE_FROM_RECENTS, INVALID_STACK_ID);
+ if (tr != null) {
+ tr.removeTaskActivitiesLocked();
+ cleanUpRemovedTaskLocked(tr, killProcess, removeFromRecents);
+ if (tr.isPersistable) {
+ mService.notifyTaskPersisterLocked(null, true);
+ }
+ return true;
+ }
+ Slog.w(TAG, "Request to remove task ignored for non-existent task " + taskId);
+ return false;
+ }
+
+ void cleanUpRemovedTaskLocked(TaskRecord tr, boolean killProcess, boolean removeFromRecents) {
+ if (removeFromRecents) {
+ mRecentTasks.remove(tr);
+ tr.removedFromRecents();
+ }
+ ComponentName component = tr.getBaseIntent().getComponent();
+ if (component == null) {
+ Slog.w(TAG, "No component for base intent of task: " + tr);
+ return;
+ }
+
+ // Find any running services associated with this app and stop if needed.
+ mService.mServices.cleanUpRemovedTaskLocked(tr, component, new Intent(tr.getBaseIntent()));
+
+ if (!killProcess) {
+ return;
+ }
+
+ // Determine if the process(es) for this task should be killed.
+ final String pkg = component.getPackageName();
+ ArrayList<ProcessRecord> procsToKill = new ArrayList<>();
+ ArrayMap<String, SparseArray<ProcessRecord>> pmap = mService.mProcessNames.getMap();
+ for (int i = 0; i < pmap.size(); i++) {
+
+ SparseArray<ProcessRecord> uids = pmap.valueAt(i);
+ for (int j = 0; j < uids.size(); j++) {
+ ProcessRecord proc = uids.valueAt(j);
+ if (proc.userId != tr.userId) {
+ // Don't kill process for a different user.
+ continue;
+ }
+ if (proc == mService.mHomeProcess) {
+ // Don't kill the home process along with tasks from the same package.
+ continue;
+ }
+ if (!proc.pkgList.containsKey(pkg)) {
+ // Don't kill process that is not associated with this task.
+ continue;
+ }
+
+ for (int k = 0; k < proc.activities.size(); k++) {
+ TaskRecord otherTask = proc.activities.get(k).task;
+ if (tr.taskId != otherTask.taskId && otherTask.inRecents) {
+ // Don't kill process(es) that has an activity in a different task that is
+ // also in recents.
+ return;
+ }
+ }
+
+ if (proc.foregroundServices) {
+ // Don't kill process(es) with foreground service.
+ return;
+ }
+
+ // Add process to kill list.
+ procsToKill.add(proc);
+ }
+ }
+
+ // Kill the running processes.
+ for (int i = 0; i < procsToKill.size(); i++) {
+ ProcessRecord pr = procsToKill.get(i);
+ if (pr.setSchedGroup == ProcessList.SCHED_GROUP_BACKGROUND
+ && pr.curReceivers.isEmpty()) {
+ pr.kill("remove task", true);
+ } else {
+ // We delay killing processes that are not in the background or running a receiver.
+ pr.waitingToKill = "remove task";
+ }
+ }
+ }
+
int getNextStackId() {
while (true) {
if (mNextFreeStackId >= FIRST_DYNAMIC_STACK_ID