From eb125180e64e1a248248977f5da30642e66b774f Mon Sep 17 00:00:00 2001
From: Aaron Kling <webgeek1234@gmail.com>
Date: Thu, 31 Dec 2020 21:38:11 +0000
Subject: [PATCH] Add nvidia profile manager

This implements and API for apps to interface with NvCPL and by
extension the PowerHAL.

Reverse engineered from the Shield Experience 8.2.0 update then cleaned
up to match Android standards.

Change-Id: I774ac62c8867151f21712cfeee25f96a591415ad
---
 .../app/ApplicationPackageManager.java        |  12 +
 .../android/content/pm/PackageManager.java    |   7 +
 .../android/content/pm/PackageParser.java     |   7 +
 core/java/com/nvidia/NvAppProfileService.java | 246 ++++++++++++++++++
 core/java/com/nvidia/NvAppProfiles.java       |  96 +++++++
 .../com/nvidia/NvCPLSvc/NvAppProfile.java     |   4 +-
 core/java/com/nvidia/NvConstants.java         |  18 ++
 core/java/com/nvidia/NvWhitelistService.java  | 112 ++++++++
 .../profilemanager/NvAppProfileSettingId.java |  93 +++++++
 .../nvidia/profilemanager/ProfileTypeId.java  |  11 +
 core/res/res/values/symbols.xml               |   1 +
 .../res/xml/tv_launcher_app_white_list.xml    |   3 +
 .../server/pm/PackageManagerService.java      |  11 +
 .../android/test/mock/MockPackageManager.java |   8 +
 14 files changed, 627 insertions(+), 2 deletions(-)
 create mode 100644 core/java/com/nvidia/NvAppProfileService.java
 create mode 100644 core/java/com/nvidia/NvAppProfiles.java
 create mode 100644 core/java/com/nvidia/NvConstants.java
 create mode 100644 core/java/com/nvidia/NvWhitelistService.java
 create mode 100644 core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java
 create mode 100644 core/java/com/nvidia/profilemanager/ProfileTypeId.java
 create mode 100644 core/res/res/xml/tv_launcher_app_white_list.xml

diff --git a/core/java/android/app/ApplicationPackageManager.java b/core/java/android/app/ApplicationPackageManager.java
index cd2c12cb4b6f..9a1051a5c1e8 100644
--- a/core/java/android/app/ApplicationPackageManager.java
+++ b/core/java/android/app/ApplicationPackageManager.java
@@ -123,6 +123,8 @@ import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.os.SomeArgs;
 import com.android.internal.util.UserIcons;
 
+import com.nvidia.NvAppProfileService;
+
 import dalvik.system.VMRuntime;
 
 import libcore.util.EmptyArray;
@@ -175,6 +177,7 @@ public class ApplicationPackageManager extends PackageManager {
     private PermissionManager mPermissionManager;
     @GuardedBy("mLock")
     private PackageInstaller mInstaller;
+    private NvAppProfileService mAppProfileService;
     @GuardedBy("mLock")
     private ArtManager mArtManager;
 
@@ -435,6 +438,15 @@ public class ApplicationPackageManager extends PackageManager {
                 com.android.internal.R.bool.config_wirelessConsentRequired);
     }
 
+    /** @hide */
+    @Override
+    public NvAppProfileService getAppProfileService() {
+        if (mAppProfileService == null) {
+            mAppProfileService = new NvAppProfileService(mContext);
+        }
+        return mAppProfileService;
+    }
+
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
diff --git a/core/java/android/content/pm/PackageManager.java b/core/java/android/content/pm/PackageManager.java
index a6bf6a41e756..f58af0e6f1c9 100644
--- a/core/java/android/content/pm/PackageManager.java
+++ b/core/java/android/content/pm/PackageManager.java
@@ -84,6 +84,8 @@ import android.util.Log;
 import com.android.internal.annotations.VisibleForTesting;
 import com.android.internal.util.ArrayUtils;
 
+import com.nvidia.NvAppProfileService;
+
 import dalvik.system.VMRuntime;
 
 import java.lang.annotation.Retention;
@@ -4699,6 +4701,11 @@ public abstract class PackageManager {
     public abstract List<PermissionGroupInfo> getAllPermissionGroups(
             @PermissionGroupInfoFlags int flags);
 
+    /** @hide */
+    public NvAppProfileService getAppProfileService() {
+        return null;
+    }
+
     /**
      * Get the platform-defined permissions which belong to a particular permission group.
      *
diff --git a/core/java/android/content/pm/PackageParser.java b/core/java/android/content/pm/PackageParser.java
index 4ff26242dab2..86fef5d9df19 100644
--- a/core/java/android/content/pm/PackageParser.java
+++ b/core/java/android/content/pm/PackageParser.java
@@ -96,6 +96,8 @@ import com.android.internal.os.ClassLoaderFactory;
 import com.android.internal.util.ArrayUtils;
 import com.android.internal.util.XmlUtils;
 
+import com.nvidia.NvAppProfileService;
+
 import libcore.io.IoUtils;
 import libcore.util.EmptyArray;
 import libcore.util.HexEncoding;
@@ -601,6 +603,7 @@ public class PackageParser {
      * a package.
      */
     public interface Callback {
+        NvAppProfileService getAppProfileService();
         boolean hasFeature(String feature);
     }
 
@@ -615,6 +618,10 @@ public class PackageParser {
             mPm = pm;
         }
 
+        @Override public NvAppProfileService getAppProfileService() {
+            return mPm.getAppProfileService();
+        }
+
         @Override public boolean hasFeature(String feature) {
             return mPm.hasSystemFeature(feature);
         }
diff --git a/core/java/com/nvidia/NvAppProfileService.java b/core/java/com/nvidia/NvAppProfileService.java
new file mode 100644
index 000000000000..694c4bd663db
--- /dev/null
+++ b/core/java/com/nvidia/NvAppProfileService.java
@@ -0,0 +1,246 @@
+/*
+ * Copyright (c) 2012 - 2014 NVIDIA Corporation.  All rights reserved.
+ *
+ * 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.
+ *
+ * Class structure based upon Camera in Camera.java:
+ * Copyright (C) 2009 The Android Open Source Project
+ */
+
+package com.nvidia;
+
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.graphics.drawable.Drawable;
+import android.os.SystemProperties;
+import android.os.UserHandle;
+import android.util.Log;
+
+import com.nvidia.profilemanager.NvAppProfileSettingId;
+
+/**
+ * @hide
+ */
+public class NvAppProfileService {
+    private static final String TAG = "NvAppProfileService";
+    private static final String APP_START_ACTION =
+            "com.nvidia.NvAppProfileService.action.APP_START";
+    private static final String APP_START_TARGET_PACKAGE = "com.nvidia.peripheralservice";
+    private static final String FEATURE_POWER_BUDGET_CONTROL =
+            "nvidia.feature.power_budget_control";
+    private static final String FEATURE_FAN_ON_DEVICE = "nvidia.feature.fan_on_device";
+
+    private final NvAppProfiles mAppProfile;
+    private final NvWhitelistService mWhitelistService;
+    private final Context mContext;
+
+    private boolean mInitialisedAppProfiles = false;
+    private boolean mFanCapEnabled = false;
+    private boolean mPbcEnabled = false;
+
+    public NvAppProfileService(Context context) {
+        Context appContext = context.getApplicationContext();
+        if (appContext == null) {
+            mContext = context;
+        } else {
+            mContext = appContext;
+        }
+
+        mAppProfile = new NvAppProfiles(mContext);
+        mWhitelistService = new NvWhitelistService(mContext);
+    }
+
+    private static String getPackageName(String appName) {
+        int index = appName.indexOf('/');
+        if (index < 0) {
+            Log.e(TAG, "appName does not contain '/'. " +
+                    "The packageName cannot be extracted from appName!");
+            return null;
+        }
+        return appName.substring(0, index);
+    }
+
+    /*
+     * These are functions that depend on NvAppProfiles and may or may not
+     * be supported for certain platforms. In the latter case, these methods
+     * should return -1.
+     */
+    public boolean getAppProfileFRCEnable(String packageName) {
+        return packageName != null && mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.VIDEO_FRC_ENABLE) == 1;
+    }
+
+    public boolean getAppProfileCreateSecureDecoder(String packageName) {
+        return packageName != null && mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.VIDEO_SECURE_DECODE) == 1;
+    }
+
+    public boolean getAppProfileTSFilterEnable(String packageName) {
+        return packageName != null && mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.VIDEO_TS_FILTERING) == 1;
+    }
+
+    public boolean getAppProfileMediaEnableMsdHal(String packageName) {
+        return packageName != null && this.mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.MEDIA_ENABLE_MSD_HAL) == 1;
+    }
+
+    public boolean getAppProfileNvidiaCertification(String packageName) {
+        return packageName != null && mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.NVIDIA_VIDEO_CERTIFICATION_ENABLED) == 1;
+    }
+
+    public String customizeAppBanner(String packageName) {
+        if (packageName == null) return null;
+
+        final String bannerName = mAppProfile.getApplicationProfileString(packageName,
+                NvAppProfileSettingId.WHITELIST_CUSTOMIZE_BANNER);
+        if (bannerName != null) return bannerName;
+
+        return mWhitelistService.getBannerName(packageName);
+    }
+
+    public Drawable getBannerDrawable(String packageName) {
+        final String bannerName = customizeAppBanner(packageName);
+        if (bannerName == null || bannerName.length() == 0) {
+            return null;
+        }
+
+        final Resources systemResources = mContext.getResources().getSystem();
+        final int drawableResourceId = systemResources.getIdentifier(bannerName,
+                "drawable", "android");
+        if (drawableResourceId == 0) return null;
+
+        return systemResources.getDrawable(drawableResourceId);
+    }
+
+    public NvWhitelistService getWhitelistService() {
+        return mWhitelistService;
+    }
+
+    public boolean getAppProfileDisableApp(String packageName) {
+        return packageName != null && mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.DISABLE_APP) == 1;
+    }
+
+    private int getAppProfileCpuScalingMinFreq(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.SCALING_MIN_FREQ);
+    }
+
+    private int getAppProfileCpuCoreBias(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.CORE_BIAS);
+    }
+
+    private int getAppProfileGpuScaling(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.GPU_SCALING);
+    }
+
+    private int getAppProfileCpuMaxNormalFreq(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.CPU_FREQ_BIAS);
+    }
+
+    private int getAppProfileCpuMaxNormalFreqPercent(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.MAX_CPU_FREQ_PCT);
+    }
+
+    private int getAppProfileCpuMinCore(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.MIN_CPU_CORES);
+    }
+
+    private int getAppProfileCpuMaxCore(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.MAX_CPU_CORES);
+    }
+
+    private int getAppProfileGpuCbusCapLevel(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.GPU_CORE_CAP);
+    }
+
+    private int getAppProfileEdpMode(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.EDP_MODE);
+    }
+
+    private int getAppProfilePbcPwr(String packageName) {
+        if (!mPbcEnabled) return -1;
+
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.PBC_PWR_LIMIT);
+    }
+
+    private int getAppProfileFanCap(String packageName) {
+        if (!mFanCapEnabled) return -1;
+
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.FAN_PWM_CAP);
+    }
+
+    private int getAppProfileVoltTempMode(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.VOLT_TEMP_MODE);
+    }
+
+    private int getAppProfileAggresivePrismEnable(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.AGGRESSIVE_PRISM_ENABLE);
+    }
+
+    private int getAppProfileDevicePowerMode(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName,
+                NvAppProfileSettingId.SYSTEM_POWER_MODE);
+    }
+
+    public String getAppProfileRegionEnableList(String packageName) {
+        return mAppProfile.getApplicationProfileString(packageName,
+                NvAppProfileSettingId.SET_REGION_LIST);
+    }
+
+    public int getAppProfileNvidiaBBCApps(String packageName) {
+        return mAppProfile.getApplicationProfile(packageName, NvAppProfileSettingId.BBC_APPS);
+    }
+
+    private int retrievePowerMode() {
+        final String powerMode = SystemProperties.get(NvConstants.NvPowerModeProperty);
+        if (powerMode != null) {
+            try {
+                return Integer.parseInt(powerMode);
+            } catch (NumberFormatException ex) {
+                // Fallthrough to error case
+            }
+        }
+
+        return -1;
+    }
+
+    /**
+     * Interface for the caller
+     */
+    public void setAppProfile(String packageName) {
+        // Greedy initialization of App Profiles
+        if (!mInitialisedAppProfiles) {
+            PackageManager pm = mContext.getPackageManager();
+            mPbcEnabled = pm.hasSystemFeature(FEATURE_POWER_BUDGET_CONTROL);
+            mFanCapEnabled = pm.hasSystemFeature(FEATURE_FAN_ON_DEVICE);
+
+            Log.w(TAG, "Enabled");
+            mInitialisedAppProfiles = true;
+        }
+
+        mAppProfile.powerHint(packageName);
+
+        Intent intent = new Intent(APP_START_ACTION);
+        intent.setPackage(APP_START_TARGET_PACKAGE);
+        intent.putExtra("AppStartId", packageName);
+        mContext.sendBroadcastAsUser(intent, UserHandle.CURRENT,
+                "nvidia.permission.READ_APP_START_INFO");
+    }
+}
diff --git a/core/java/com/nvidia/NvAppProfiles.java b/core/java/com/nvidia/NvAppProfiles.java
new file mode 100644
index 000000000000..1c88b725afd7
--- /dev/null
+++ b/core/java/com/nvidia/NvAppProfiles.java
@@ -0,0 +1,96 @@
+package com.nvidia;
+
+import android.content.Context;
+import android.content.Intent;
+import android.os.IBinder;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+
+import com.nvidia.NvCPLSvc.INvCPLRemoteService;
+
+public class NvAppProfiles {
+    /**
+     * Unique name used for NvCPLSvc to whitelist this class
+     */
+    static final String NV_APP_PROFILES_NAME = "Frameworks_NvAppProfiles";
+    static final boolean DEBUG = false;
+    private static final String TAG = "NvAppProfiles";
+    private final Context mContext;
+    private INvCPLRemoteService mNvCPLSvc = null;
+    private IBinder mNvCPLSvcBinder = null;
+
+    /**
+     * Callback class given by the NvCPLService
+     */
+
+    public NvAppProfiles(Context context) {
+        mContext = context;
+    }
+
+    public int getApplicationProfile(String packageName, int settingId) {
+        getNvCPLService();
+        if (mNvCPLSvc != null) {
+            try {
+                return mNvCPLSvc.getAppProfileSettingInt(packageName, settingId);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to retrieve profile setting. Error=" + ex.getMessage());
+            }
+        }
+
+        return -1;
+    }
+
+    public String getApplicationProfileString(String packageName, int settingId) {
+        getNvCPLService();
+        if (mNvCPLSvc != null) {
+            try {
+                return mNvCPLSvc.getAppProfileSettingString(packageName, settingId);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to retrieve profile setting. Error=" + ex.getMessage());
+            }
+        }
+
+        return null;
+    }
+
+    public void setPowerMode(int index) {
+        if (DEBUG) Log.w(TAG, "Setting power mode: " + String.valueOf(index));
+
+        Intent intent = new Intent();
+        intent.setClassName(NvConstants.NvCPLSvc, NvConstants.NvCPLService);
+        intent.putExtra(NvConstants.NvOrigin, 1);
+        intent.putExtra(NvConstants.NvPowerMode , String.valueOf(index));
+
+        handleIntent(intent);
+    }
+
+    public void powerHint(String packageName) {
+        getNvCPLService();
+        if (mNvCPLSvc != null) {
+            try {
+                mNvCPLSvc.powerHint(packageName);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to send power hint. Error=" + ex.getMessage());
+            }
+        }
+    }
+
+    public void handleIntent(Intent intent) {
+        getNvCPLService();
+        if (mNvCPLSvc != null) {
+            try {
+                mNvCPLSvc.handleIntent(intent);
+            } catch (RemoteException ex) {
+                Log.w(TAG, "Failed to handle intent. Error=" + ex.getMessage());
+            }
+        }
+    }
+
+    private void getNvCPLService() {
+        if (mNvCPLSvc == null || mNvCPLSvcBinder == null || !mNvCPLSvcBinder.isBinderAlive()) {
+            mNvCPLSvcBinder = ServiceManager.getService("nvcpl");
+            mNvCPLSvc = INvCPLRemoteService.Stub.asInterface(mNvCPLSvcBinder);
+        }
+    }
+}
diff --git a/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java
index 82986fbb7ca6..1486edbe45c1 100644
--- a/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java
+++ b/core/java/com/nvidia/NvCPLSvc/NvAppProfile.java
@@ -41,11 +41,11 @@ public class NvAppProfile implements Parcelable {
     }
 
     private static String encodeNull(String string) {
-        return string != null ? string : ProxyInfo.LOCAL_EXCL_LIST;
+        return string != null ? string : "";
     }
 
     private static String decodeNull(String string) {
-        return !string.equals(ProxyInfo.LOCAL_EXCL_LIST) ? string : null;
+        return !string.equals("") ? string : null;
     }
 
     public int describeContents() {
diff --git a/core/java/com/nvidia/NvConstants.java b/core/java/com/nvidia/NvConstants.java
new file mode 100644
index 000000000000..d424d4cf28b3
--- /dev/null
+++ b/core/java/com/nvidia/NvConstants.java
@@ -0,0 +1,18 @@
+package com.nvidia;
+
+public class NvConstants {
+    public static final String NvAppClose = "com.nvidia.app.close";
+    public static final String NvBatteryMonitor = "com.nvidia.NvCPLSvc.BatteryMonitor";
+    public static final String NvCPLService = "com.nvidia.NvCPLSvc.NvCPLService";
+    public static final String NvCPLSvc = "com.nvidia.NvCPLSvc";
+    public static final String NvMaxGpuMode = "com.nvidia.NvCPLSvc.MaxGpuMode";
+    public static final String NvOrigin = "com.nvidia.NvCPLSvc.Origin";
+    public static final String NvPowerMode = "com.nvidia.NvCPLSvc.NV_POWER_MODE";
+    public static final String NvPowerModeProperty = "persist.vendor.sys.NV_POWER_MODE";
+    public static final String NvStateId = "com.nvidia.NvCPLSvc.StateID";
+    public static final String NvTCPDivisor = "com.nvidia.NvCPLSvc.NV_TCPDIVISOR";
+    public static final String NvThermalStats = "com.nvidia.peripheralservice.NvThermalStats";
+    public static final int NV_POWER_MODE_MAX_PERF = 0;
+    public static final int NV_POWER_MODE_OPTIMIZED = 1;
+    public static final int NV_POWER_MODE_BATTERY_SAVER = 2;
+}
diff --git a/core/java/com/nvidia/NvWhitelistService.java b/core/java/com/nvidia/NvWhitelistService.java
new file mode 100644
index 000000000000..dfed390ccd91
--- /dev/null
+++ b/core/java/com/nvidia/NvWhitelistService.java
@@ -0,0 +1,112 @@
+package com.nvidia;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.util.Log;
+
+import com.android.internal.R;
+
+import java.util.ArrayList;
+import java.io.IOException;
+
+import org.json.JSONObject;
+import org.json.JSONArray;
+import org.json.JSONException;
+import org.xmlpull.v1.XmlPullParserException;
+
+public class NvWhitelistService {
+    private static final String TAG = "NvWhitelistService";
+    private JSONArray mWhiteListArray;
+    private Context mContext;
+
+    public NvWhitelistService(Context context) {
+        Context appContext = context.getApplicationContext();
+        mContext = appContext != null ? appContext : context;
+        mWhiteListArray =
+                parseXml(mContext.getResources().getXml(R.xml.tv_launcher_app_white_list));
+    }
+
+    public boolean isWhiteApp(String pkgName) {
+        if (mWhiteListArray == null) return false;
+
+        for (int i = 0; i < mWhiteListArray.length(); i++) {
+            try {
+                if (pkgName.equals(mWhiteListArray.getJSONObject(i).getString("packageName"))) {
+                    return true;
+                }
+            } catch (JSONException ex) {
+                Log.w(TAG, ex.getMessage());
+            }
+        }
+
+        return false;
+    }
+
+    public boolean isTvGame(String pkgName) {
+        if (mWhiteListArray == null) return false;
+
+        for (int i = 0; i < mWhiteListArray.length(); i++) {
+            try {
+                if (mWhiteListArray.getJSONObject(i).getString("packageName").equals(pkgName) &&
+                        mWhiteListArray.getJSONObject(i).getString("isGame").equals("true")) {
+                    return true;
+                }
+            } catch (JSONException ex) {
+                Log.w(TAG, ex.getMessage());
+            }
+        }
+
+        return false;
+    }
+
+    public String getBannerName(String pkgName) {
+        for (int i = 0; i < mWhiteListArray.length(); i++) {
+            try {
+                if (mWhiteListArray.getJSONObject(i).getString("packageName").equals(pkgName)) {
+                    return mWhiteListArray.getJSONObject(i).getString("bannerName");
+                }
+            } catch (JSONException ex) {
+                Log.w(TAG, ex.getMessage());
+            }
+        }
+
+        return "";
+    }
+
+    private JSONArray parseXml(XmlResourceParser xmlParser) {
+        if (xmlParser == null) return null;
+
+        JSONObject jsonObj = null;
+        ArrayList<JSONObject> widgetConfigs = new ArrayList<>();
+        try {
+            int type = xmlParser.getEventType();
+            while (type != XmlResourceParser.END_DOCUMENT) {
+                switch (type) {
+                    case XmlResourceParser.START_TAG:
+                        if (xmlParser.getName().equals("app")) {
+                            jsonObj = new JSONObject();
+                            for (int i = 0; i < xmlParser.getAttributeCount(); i++) {
+                                jsonObj.put(xmlParser.getAttributeName(i),
+                                        xmlParser.getAttributeValue(i));
+                            }
+                            break;
+                        }
+                        break;
+                    case XmlResourceParser.END_TAG:
+                        if (xmlParser.getName().equals("app") && jsonObj != null) {
+                            widgetConfigs.add(jsonObj);
+                            break;
+                        }
+                        break;
+                    default:
+                        break;
+                }
+                type = xmlParser.next();
+            }
+        } catch (IOException | JSONException | XmlPullParserException e) {
+            e.printStackTrace();
+        }
+
+        return new JSONArray(widgetConfigs);
+    }
+}
diff --git a/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java b/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java
new file mode 100644
index 000000000000..0c78f2b8dc57
--- /dev/null
+++ b/core/java/com/nvidia/profilemanager/NvAppProfileSettingId.java
@@ -0,0 +1,93 @@
+package com.nvidia.profilemanager;
+
+public class NvAppProfileSettingId {
+    public static int RESOVERRIDE_SCALE_FACTOR = 5;
+    public static int STEREO_PERF_WIDTH = 7;
+    public static int RESOVERRIDE_PERF_WIDTH = 8;
+    public static int FORCE_HW_UI = 9;
+    public static int CORE_BIAS = 17;
+    public static int CPU_FREQ_BIAS = 18;
+    public static int GPU_CORE_CAP = 19;
+    public static int SCALING_MIN_FREQ = 20;
+    public static int GPU_SCALING = 21;
+    public static int PBC_PWR_LIMIT = 22;
+    public static int FAN_PWM_CAP = 23;
+    public static int VOLT_TEMP_MODE = 24;
+    public static int FRAME_RATE_LIMIT = 26;
+    public static int DISABLE_APM = 29;
+    public static int EDP_MODE = 30;
+    public static int GPU_MODE = 35;
+    public static int STYLUS_FINGER_ONLY_MODE = 40;
+    public static int STEREO_PERF_SCALE_FACTOR = 43;
+    public static int DISABLE_BUFFER_AGE = 47;
+    public static int SYSTEM_POWER_MODE = 48;
+    public static int CUSTOM_PROFILE_BLACKLIST = 51;
+    public static int BLOCK_NETWORK_ACCESS = 53;
+    public static int MAX_CPU_CORES = 58;
+    public static int AGGRESSIVE_PRISM_ENABLE = 59;
+    public static int BLOCK_ON_NETWORK = 60;
+    public static int MAX_CPU_FREQ_PCT = 61;
+    public static int DEPTH_COMPRESSION = 62;
+    public static int OGL_THREADCONTROL = 63;
+    public static int OGL_SKIP_ENABLED_UNSET_ARRAY = 64;
+    public static int CMU_GTM_MAPPING_LUT = 65;
+    public static int OGL_CLEAR_METERING = 66;
+    public static int KILL_PROCESS_BELOW_ADJ = 67;
+    public static int EGL_REPORT_ES1_CONFIG_ONLY = 69;
+    public static int VIDEO_IQ = 70;
+    public static int CAMERA_MEMORY = 71;
+    public static int PERF_FP = 72;
+    public static int MIN_CPU_CORES = 73;
+    public static int PROMOTE_SURFACE_TO_32BIT = 74;
+    public static int AFFINITY_DAEMON_ENABLE = 75;
+    public static int CUSTOM_PROFILE_SYSTEM_WHITELIST = 76;
+    public static int FORCEONCPU = 77;
+    public static int KEY_DEVICE_ID_HASH = 78;
+    public static int GSYNC_WHITELIST = 79;
+    public static int GSYNC_BLACKLIST = 80;
+    public static int TOUCH_MODE = 81;
+    public static int DISPLAY_UPSCALE_HEIGHT = 82;
+    public static int DISPLAY_UPSCALE_WIDTH = 83;
+    public static int OGL_APP_RUNTIME_VERTEX_ATTRIB_SOURCING = 84;
+    public static int MAXWELL_TILEDCACHE = 85;
+    public static int OGL_HIDE_EXTENSIONS_STRING = 86;
+    public static int NVIDIA_WEBVIEW = 87;
+    public static int OGL_HIDE_EXTENSIONS = 88;
+    public static int HDD_DIALOG_FREQ = 89;
+    public static int HDD_DIALOG_TEXT = 90;
+    public static int HDD_DIALOG_THRESHOLD = 91;
+    public static int HDD_DIALOG_ENABLE = 92;
+    public static int TCP_DIVISOR = 93;
+    public static int BBC_APPS = 94;
+    public static int HDD_STATS_ENABLE = 95;
+    public static int HDD_DIALOG_REMIND_ME_LATER_FREQ = 96;
+    public static int HDD_STATS_FREQ = 97;
+    public static int HDD_DIALOG_STR_THRESHOLD = 98;
+    public static int OGL_VERSION_OVERRIDE = 99;
+    public static int OGL_ES_VERSION_OVERRIDE = 100;
+    public static int MULTI_CHANNEL_SWITCH_MODE = 101;
+    public static int VIDEO_FRC_ENABLE = 102;
+    public static int VIDEO_SECURE_DECODE = 103;
+    public static int VIDEO_TS_FILTERING = 104;
+    public static int NVIDIA_VIDEO_CERTIFICATION_ENABLED = 105;
+    public static int OGL_SHADER_PORTABILITY_WARNINGS = 106;
+    public static int OGL_THREADCONTROL2 = 107;
+    public static int OGL_EXTRA_CGC_OPTION = 108;
+    public static int DISABLE_APP = 109;
+    public static int SET_REGION_LIST = 110;
+    public static int PINNING_ENABLE = 111;
+    public static int PINNING_ORDER = 112;
+    public static int GPU_MODESET_ENABLE = 113;
+    public static int AVS_DELAY_ENABLE = 114;
+    public static int BLACKLIST_USB_30 = 115;
+    public static int DRM_DIALOG_ENABLE = 116;
+    public static int FILTER_EGLCONFIGS = 117;
+    public static int SHIELD_LOGGING = 118;
+    public static int APP_AUDIO_SWITCH_TO_STEREO = 119;
+    public static int WHITELIST_CUSTOMIZE_BANNER = 120;
+    public static int NV_MAPPER_GAME_LIST = 121;
+    public static int NV_MULTI_CAPTURE_MOD = 122;
+    public static int MEDIA_ENABLE_MSD_HAL = 123;
+    public static int DEEPISP_DISABLED = 124;
+    public static int OGL_GPFIFO_SIZE_ENTRIES = 125;
+}
diff --git a/core/java/com/nvidia/profilemanager/ProfileTypeId.java b/core/java/com/nvidia/profilemanager/ProfileTypeId.java
new file mode 100644
index 000000000000..db85b0fc577f
--- /dev/null
+++ b/core/java/com/nvidia/profilemanager/ProfileTypeId.java
@@ -0,0 +1,11 @@
+package com.nvidia.profilemanager;
+
+public class ProfileTypeId {
+    public static int LAST_RESERVED_TYPE = 9999;
+
+    public static int DEFAULT = 0;
+    public static int OEM_OVERRIDE = 1;
+    public static int DEV_OVERRIDE = 2;
+    public static int USER = 10000;
+    public static int OPTIMIZED = 10001;
+}
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 3f53f246a8a4..a0be9081e428 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -1617,6 +1617,7 @@
   <java-symbol type="xml" name="default_zen_mode_config" />
   <java-symbol type="xml" name="sms_7bit_translation_table" />
   <java-symbol type="xml" name="color_extraction" />
+  <java-symbol type="xml" name="tv_launcher_app_white_list" />
 
   <java-symbol type="raw" name="color_fade_vert" />
   <java-symbol type="raw" name="color_fade_frag" />
diff --git a/core/res/res/xml/tv_launcher_app_white_list.xml b/core/res/res/xml/tv_launcher_app_white_list.xml
new file mode 100644
index 000000000000..e2b226327686
--- /dev/null
+++ b/core/res/res/xml/tv_launcher_app_white_list.xml
@@ -0,0 +1,3 @@
+<?xml version="1.0" encoding="utf-8"?>
+<black-list-apps>
+</black-list-apps>
diff --git a/services/core/java/com/android/server/pm/PackageManagerService.java b/services/core/java/com/android/server/pm/PackageManagerService.java
index 1fc23578b46d..42e9aa9005df 100644
--- a/services/core/java/com/android/server/pm/PackageManagerService.java
+++ b/services/core/java/com/android/server/pm/PackageManagerService.java
@@ -412,6 +412,8 @@ import com.android.server.utils.WatchedSparseIntArray;
 import com.android.server.utils.Watcher;
 import com.android.server.wm.ActivityTaskManagerInternal;
 
+import com.nvidia.NvAppProfileService;
+
 import dalvik.system.CloseGuard;
 import dalvik.system.VMRuntime;
 
@@ -1455,6 +1457,8 @@ public class PackageManagerService extends IPackageManager.Stub
 
     final PackageInstallerService mInstallerService;
 
+    private NvAppProfileService mAppProfileService;
+
     final ArtManagerService mArtManagerService;
 
     final PackageDexOptimizer mPackageDexOptimizer;
@@ -7420,6 +7424,13 @@ public class PackageManagerService extends IPackageManager.Stub
             public boolean hasFeature(String feature) {
                 return PackageManagerService.this.hasSystemFeature(feature, 0);
             }
+
+            public NvAppProfileService getAppProfileService() {
+                if (mAppProfileService == null) {
+                    mAppProfileService = new NvAppProfileService(mContext);
+                }
+                return mAppProfileService;
+            }
         };
 
         // CHECKSTYLE:ON IndentationCheck
diff --git a/test-mock/src/android/test/mock/MockPackageManager.java b/test-mock/src/android/test/mock/MockPackageManager.java
index 5f95bc124de6..1f6aedfb8d52 100644
--- a/test-mock/src/android/test/mock/MockPackageManager.java
+++ b/test-mock/src/android/test/mock/MockPackageManager.java
@@ -55,6 +55,8 @@ import android.os.PersistableBundle;
 import android.os.UserHandle;
 import android.os.storage.VolumeInfo;
 
+import com.nvidia.NvAppProfileService;
+
 import java.util.List;
 import java.util.Set;
 
@@ -178,6 +180,12 @@ public class MockPackageManager extends PackageManager {
         throw new UnsupportedOperationException();
     }
 
+    /** @hide */
+    @Override
+    public NvAppProfileService getAppProfileService() {
+        throw new UnsupportedOperationException();
+    }
+
     @Override
     public ApplicationInfo getApplicationInfo(String packageName, int flags)
             throws NameNotFoundException {
-- 
GitLab