ProcessCompat.java
/*
* Copyright 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package androidx.core.os;
import android.annotation.SuppressLint;
import android.os.Build;
import android.os.Process;
import android.os.UserHandle;
import androidx.annotation.RequiresApi;
import java.lang.reflect.Method;
/**
* Helper for accessing features in {@link Process}.
*/
public final class ProcessCompat {
private ProcessCompat() {
// This class is non-instantiable.
}
/**
* Returns whether the given {@code uid} belongs to an application.
*
* Compatibility behavior:
* <ul>
* <li>SDK 24 and above, this method matches platform behavior.
* <li>SDK 16 through 23, this method is a best-effort to match platform behavior, but may
* default to returning {@code true} if an accurate result is not available.
* <li>SDK 15 and below, this method always returns {@code true} as application UIDs and
* isolated processes did not exist yet.
* </ul>
*
* @param uid a kernel uid
* @return {@code true} if the uid corresponds to an application sandbox running in a
* specific user, {@code false} if the uid corresponds to an isolated user ID process or
* does not otherwise correspond to an application user ID, or a value based on
* platform-specific fallback behavior
*/
public static boolean isApplicationUid(int uid) {
if (Build.VERSION.SDK_INT >= 24) {
return Api24Impl.isApplicationUid(uid);
} else if (Build.VERSION.SDK_INT >= 17) {
return Api17Impl.isApplicationUid(uid);
} else if (Build.VERSION.SDK_INT == 16) {
return Api16Impl.isApplicationUid(uid);
} else {
return true;
}
}
@RequiresApi(24)
static class Api24Impl {
private Api24Impl() {
// This class is non-instantiable.
}
static boolean isApplicationUid(int uid) {
// In N, the method was made public on android.os.Process.
return Process.isApplicationUid(uid);
}
}
@RequiresApi(17)
static class Api17Impl {
private static final Object sResolvedLock = new Object();
private static Method sMethodUserHandleIsAppMethod;
private static boolean sResolved;
private Api17Impl() {
// This class is non-instantiable.
}
@SuppressWarnings({"JavaReflectionMemberAccess", "CatchAndPrintStackTrace"})
@SuppressLint("DiscouragedPrivateApi")
static boolean isApplicationUid(int uid) {
// In JELLY_BEAN_MR2, the equivalent isApp(int) hidden method moved to public class
// android.os.UserHandle.
try {
synchronized (sResolvedLock) {
if (!sResolved) {
sResolved = true;
sMethodUserHandleIsAppMethod = UserHandle.class.getDeclaredMethod("isApp",
int.class);
}
}
if (sMethodUserHandleIsAppMethod != null) {
Boolean result = (Boolean) sMethodUserHandleIsAppMethod.invoke(null, uid);
if (result == null) {
// This should never happen, as the method returns a boolean primitive.
throw new NullPointerException();
}
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
@RequiresApi(16)
static class Api16Impl {
private static final Object sResolvedLock = new Object();
private static Method sMethodUserIdIsAppMethod;
private static boolean sResolved;
private Api16Impl() {
// This class is non-instantiable.
}
@SuppressLint("PrivateApi")
@SuppressWarnings("CatchAndPrintStackTrace")
static boolean isApplicationUid(int uid) {
// In JELLY_BEAN_MR1, the equivalent isApp(int) hidden method was available on hidden
// class android.os.UserId.
try {
synchronized (sResolvedLock) {
if (!sResolved) {
sResolved = true;
sMethodUserIdIsAppMethod = Class.forName("android.os.UserId")
.getDeclaredMethod("isApp", int.class);
}
}
if (sMethodUserIdIsAppMethod != null) {
Boolean result = (Boolean) sMethodUserIdIsAppMethod.invoke(null, uid);
if (result == null) {
// This should never happen, as the method returns a boolean primitive.
throw new NullPointerException();
}
return result;
}
} catch (Exception e) {
e.printStackTrace();
}
return true;
}
}
}