Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 1e056671 authored by Winson Chiu's avatar Winson Chiu Committed by Android (Google) Code Review
Browse files

Merge "Overlayable actor enforcement"

parents 5d1c9a38 d9d17367
Loading
Loading
Loading
Loading
+120 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 android.content.om;

import android.annotation.NonNull;
import android.annotation.Nullable;

import com.android.internal.util.DataClass;

import java.util.Objects;

/**
 * Immutable info on an overlayable defined inside a target package.
 *
 * @hide
 */
@DataClass(genSetters = false, genEqualsHashCode = true, genHiddenConstructor = true)
public final class OverlayableInfo {

    /**
     * The "name" attribute of the overlayable tag. Used to identify the set of resources overlaid.
     */
    @NonNull
    public final String name;

    /**
     * The "actor" attribute of the overlayable tag. Used to signal which apps are allowed to
     * modify overlay state for this overlayable.
     */
    @Nullable
    public final String actor;

    // CHECKSTYLE:OFF Generated code
    //



    // Code below generated by codegen v1.0.3.
    //
    // DO NOT MODIFY!
    // CHECKSTYLE:OFF Generated code
    //
    // To regenerate run:
    // $ codegen $ANDROID_BUILD_TOP/frameworks/base/core/java/android/content/om/OverlayableInfo.java


    /**
     * Creates a new OverlayableInfo.
     *
     * @param name
     *   The "name" attribute of the overlayable tag. Used to identify the set of resources overlaid.
     * @param actor
     *   The "actor" attribute of the overlayable tag. Used to signal which apps are allowed to
     *   modify overlay state for this overlayable.
     * @hide
     */
    @DataClass.Generated.Member
    public OverlayableInfo(
            @NonNull String name,
            @Nullable String actor) {
        this.name = name;
        com.android.internal.util.AnnotationValidations.validate(
                NonNull.class, null, name);
        this.actor = actor;

        // onConstructed(); // You can define this method to get a callback
    }

    @Override
    @DataClass.Generated.Member
    public boolean equals(Object o) {
        // You can override field equality logic by defining either of the methods like:
        // boolean fieldNameEquals(OverlayableInfo other) { ... }
        // boolean fieldNameEquals(FieldType otherValue) { ... }

        if (this == o) return true;
        if (o == null || getClass() != o.getClass()) return false;
        @SuppressWarnings("unchecked")
        OverlayableInfo that = (OverlayableInfo) o;
        //noinspection PointlessBooleanExpression
        return true
                && Objects.equals(name, that.name)
                && Objects.equals(actor, that.actor);
    }

    @Override
    @DataClass.Generated.Member
    public int hashCode() {
        // You can override field hashCode logic by defining methods like:
        // int fieldNameHashCode() { ... }

        int _hash = 1;
        _hash = 31 * _hash + Objects.hashCode(name);
        _hash = 31 * _hash + Objects.hashCode(actor);
        return _hash;
    }

    @DataClass.Generated(
            time = 1570059850579L,
            codegenVersion = "1.0.3",
            sourceFile = "frameworks/base/core/java/android/content/om/OverlayableInfo.java",
            inputSignatures = "public final @android.annotation.NonNull java.lang.String name\npublic final @android.annotation.Nullable java.lang.String actor\nclass OverlayableInfo extends java.lang.Object implements []\n@com.android.internal.util.DataClass(genSetters=false, genEqualsHashCode=true, genHiddenConstructor=true)")
    @Deprecated
    private void __metadata() {}

}
+15 −0
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package android.content.res;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.annotation.UnsupportedAppUsage;
import android.content.om.OverlayableInfo;
import android.content.res.loader.ResourcesProvider;
import android.text.TextUtils;

@@ -254,6 +255,17 @@ public final class ApkAssets {
        }
    }

    /** @hide */
    @Nullable
    public OverlayableInfo getOverlayableInfo(String overlayableName) throws IOException {
        return nativeGetOverlayableInfo(mNativePtr, overlayableName);
    }

    /** @hide */
    public boolean definesOverlayable() throws IOException {
        return nativeDefinesOverlayable(mNativePtr);
    }

    /**
     * Returns false if the underlying APK was changed since this ApkAssets was loaded.
     */
@@ -305,4 +317,7 @@ public final class ApkAssets {
    private static native long nativeGetStringBlock(long ptr);
    private static native boolean nativeIsUpToDate(long ptr);
    private static native long nativeOpenXml(long ptr, @NonNull String fileName) throws IOException;
    private static native @Nullable OverlayableInfo nativeGetOverlayableInfo(long ptr,
            String overlayableName) throws IOException;
    private static native boolean nativeDefinesOverlayable(long ptr) throws IOException;
}
+51 −1
Original line number Diff line number Diff line
@@ -18,6 +18,7 @@ package com.android.server;

import static com.android.internal.util.ArrayUtils.appendInt;

import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.ComponentName;
import android.content.pm.FeatureInfo;
@@ -221,6 +222,12 @@ public class SystemConfig {
    private ArrayMap<String, Set<String>> mPackageToUserTypeWhitelist = new ArrayMap<>();
    private ArrayMap<String, Set<String>> mPackageToUserTypeBlacklist = new ArrayMap<>();

    /**
     * Map of system pre-defined, uniquely named actors; keys are namespace,
     * value maps actor name to package name.
     */
    private ArrayMap<String, ArrayMap<String, String>> mNamedActors = null;

    public static SystemConfig getInstance() {
        if (!isSystemProcess()) {
            Slog.wtf(TAG, "SystemConfig is being accessed by a process other than "
@@ -398,12 +405,17 @@ public class SystemConfig {
        return r;
    }

    @NonNull
    public Map<String, ? extends Map<String, String>> getNamedActors() {
        return mNamedActors != null ? mNamedActors : Collections.emptyMap();
    }

    /**
     * Only use for testing. Do NOT use in production code.
     * @param readPermissions false to create an empty SystemConfig; true to read the permissions.
     */
    @VisibleForTesting
    protected SystemConfig(boolean readPermissions) {
    public SystemConfig(boolean readPermissions) {
        if (readPermissions) {
            Slog.w(TAG, "Constructing a test SystemConfig");
            readAllPermissions();
@@ -1028,6 +1040,44 @@ public class SystemConfig {
                        readInstallInUserType(parser,
                                mPackageToUserTypeWhitelist, mPackageToUserTypeBlacklist);
                    } break;
                    case "named-actor": {
                        String namespace = TextUtils.safeIntern(
                                parser.getAttributeValue(null, "namespace"));
                        String actorName = parser.getAttributeValue(null, "name");
                        String pkgName = TextUtils.safeIntern(
                                parser.getAttributeValue(null, "package"));
                        if (TextUtils.isEmpty(namespace)) {
                            Slog.wtf(TAG, "<" + name + "> without namespace in " + permFile
                                    + " at " + parser.getPositionDescription());
                        } else if (TextUtils.isEmpty(actorName)) {
                            Slog.wtf(TAG, "<" + name + "> without actor name in " + permFile
                                    + " at " + parser.getPositionDescription());
                        } else if (TextUtils.isEmpty(pkgName)) {
                            Slog.wtf(TAG, "<" + name + "> without package name in " + permFile
                                    + " at " + parser.getPositionDescription());
                        } else if ("android".equalsIgnoreCase(namespace)) {
                            throw new IllegalStateException("Defining " + actorName + " as "
                                    + pkgName + " for the android namespace is not allowed");
                        } else {
                            if (mNamedActors == null) {
                                mNamedActors = new ArrayMap<>();
                            }

                            ArrayMap<String, String> nameToPkgMap = mNamedActors.get(namespace);
                            if (nameToPkgMap == null) {
                                nameToPkgMap = new ArrayMap<>();
                                mNamedActors.put(namespace, nameToPkgMap);
                            } else if (nameToPkgMap.containsKey(actorName)) {
                                String existing = nameToPkgMap.get(actorName);
                                throw new IllegalStateException("Duplicate actor definition for "
                                        + namespace + "/" + actorName
                                        + "; defined as both " + existing + " and " + pkgName);
                            }

                            nameToPkgMap.put(actorName, pkgName);
                        }
                        XmlUtils.skipCurrentTag(parser);
                    } break;
                    default: {
                        Slog.w(TAG, "Tag " + name + " is unknown in "
                                + permFile + " at " + parser.getPositionDescription());
+56 −0
Original line number Diff line number Diff line
@@ -194,6 +194,59 @@ static jlong NativeOpenXml(JNIEnv* env, jclass /*clazz*/, jlong ptr, jstring fil
  return reinterpret_cast<jlong>(xml_tree.release());
}

static jobject NativeGetOverlayableInfo(JNIEnv* env, jclass /*clazz*/, jlong ptr,
                                         jstring overlayable_name) {
  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);

  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
  if (packages.empty()) {
    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
    return 0;
  }

  // TODO(b/119899133): Convert this to a search for the info rather than assuming it's at index 0
  const auto& overlayable_map = packages[0]->GetOverlayableMap();
  if (overlayable_map.empty()) {
    return nullptr;
  }

  auto overlayable_name_native = std::string(env->GetStringUTFChars(overlayable_name, NULL));
  auto actor = overlayable_map.find(overlayable_name_native);
  if (actor == overlayable_map.end()) {
    return nullptr;
  }

  jstring actor_string = env->NewStringUTF(actor->first.c_str());
  if (env->ExceptionCheck() || actor_string == nullptr) {
    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
    return 0;
  }

  jclass overlayable_class = env->FindClass("android/content/om/OverlayableInfo");
  jmethodID overlayable_constructor = env->GetMethodID(overlayable_class, "<init>",
                                                       "(Ljava/lang/String;Ljava/lang/String;I)V");
  return env->NewObject(
      overlayable_class,
      overlayable_constructor,
      overlayable_name,
      actor_string
  );
}

static jboolean NativeDefinesOverlayable(JNIEnv* env, jclass /*clazz*/, jlong ptr) {
  const ApkAssets* apk_assets = reinterpret_cast<const ApkAssets*>(ptr);

  const auto& packages = apk_assets->GetLoadedArsc()->GetPackages();
  if (packages.empty()) {
    // Must throw to prevent bypass by returning false
    jniThrowException(env, "java/io/IOException", "Error reading overlayable from APK");
    return 0;
  }

  const auto& overlayable_infos = packages[0]->GetOverlayableMap();
  return overlayable_infos.empty() ? JNI_FALSE : JNI_TRUE;
}

// JNI registration.
static const JNINativeMethod gApkAssetsMethods[] = {
    {"nativeLoad", "(Ljava/lang/String;ZZZZ)J", (void*)NativeLoad},
@@ -208,6 +261,9 @@ static const JNINativeMethod gApkAssetsMethods[] = {
    {"nativeGetStringBlock", "(J)J", (void*)NativeGetStringBlock},
    {"nativeIsUpToDate", "(J)Z", (void*)NativeIsUpToDate},
    {"nativeOpenXml", "(JLjava/lang/String;)J", (void*)NativeOpenXml},
    {"nativeGetOverlayableInfo", "(JLjava/lang/String;)Landroid/content/om/OverlayableInfo;",
     (void*)NativeGetOverlayableInfo},
    {"nativeDefinesOverlayable", "(J)Z", (void*)NativeDefinesOverlayable},
};

int register_android_content_res_ApkAssets(JNIEnv* env) {
+2 −0
Original line number Diff line number Diff line
@@ -273,6 +273,8 @@ class LoadedPackage {
  ByteBucketArray<uint32_t> resource_ids_;
  std::vector<DynamicPackageEntry> dynamic_package_map_;
  std::vector<const std::pair<OverlayableInfo, std::unordered_set<uint32_t>>> overlayable_infos_;

  // A map of overlayable name to actor
  std::unordered_map<std::string, std::string> overlayable_map_;
};

Loading