Skip to content

Most visited

Recently visited

navigation
MediaBrowserService / src / com.example.android.mediabrowserservice /

PackageValidator.java

1
/*
2
 * Copyright (C) 2014 The Android Open Source Project
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at
7
 *
8
 *      http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
package com.example.android.mediabrowserservice;
17
 
18
import android.content.Context;
19
import android.content.pm.PackageInfo;
20
import android.content.pm.PackageManager;
21
import android.content.res.XmlResourceParser;
22
import android.os.Process;
23
import android.util.Base64;
24
 
25
import com.example.android.mediabrowserservice.utils.LogHelper;
26
 
27
import org.xmlpull.v1.XmlPullParserException;
28
 
29
import java.io.IOException;
30
import java.util.ArrayList;
31
import java.util.HashMap;
32
import java.util.Map;
33
 
34
/**
35
 * Validates that the calling package is authorized to browse a
36
 * {@link android.service.media.MediaBrowserService}.
37
 *
38
 * The list of allowed signing certificates and their corresponding package names is defined in
39
 * res/xml/allowed_media_browser_callers.xml.
40
 */
41
public class PackageValidator {
42
    private static final String TAG = LogHelper.makeLogTag(PackageValidator.class);
43
 
44
    /**
45
     * Map allowed callers' certificate keys to the expected caller information.
46
     *
47
     */
48
    private final Map<String, ArrayList<CallerInfo>> mValidCertificates;
49
 
50
    public PackageValidator(Context ctx) {
51
        mValidCertificates = readValidCertificates(ctx.getResources().getXml(
52
            R.xml.allowed_media_browser_callers));
53
    }
54
 
55
    private Map<String, ArrayList<CallerInfo>> readValidCertificates(XmlResourceParser parser) {
56
        HashMap<String, ArrayList<CallerInfo>> validCertificates = new HashMap<>();
57
        try {
58
            int eventType = parser.next();
59
            while (eventType != XmlResourceParser.END_DOCUMENT) {
60
                if (eventType == XmlResourceParser.START_TAG
61
                        && parser.getName().equals("signing_certificate")) {
62
 
63
                    String name = parser.getAttributeValue(null, "name");
64
                    String packageName = parser.getAttributeValue(null, "package");
65
                    boolean isRelease = parser.getAttributeBooleanValue(null, "release", false);
66
                    String certificate = parser.nextText().replaceAll("\\s|\\n", "");
67
 
68
                    CallerInfo info = new CallerInfo(name, packageName, isRelease, certificate);
69
 
70
                    ArrayList<CallerInfo> infos = validCertificates.get(certificate);
71
                    if (infos == null) {
72
                        infos = new ArrayList<>();
73
                        validCertificates.put(certificate, infos);
74
                    }
75
                    LogHelper.v(TAG, "Adding allowed caller: ", info.name,
76
                        " package=", info.packageName, " release=", info.release,
77
                        " certificate=", certificate);
78
                    infos.add(info);
79
                }
80
                eventType = parser.next();
81
            }
82
        } catch (XmlPullParserException | IOException e) {
83
            LogHelper.e(TAG, e, "Could not read allowed callers from XML.");
84
        }
85
        return validCertificates;
86
    }
87
 
88
    /**
89
     * @return false if the caller is not authorized to get data from this MediaBrowserService
90
     */
91
    @SuppressWarnings("BooleanMethodIsAlwaysInverted")
92
    public boolean isCallerAllowed(Context context, String callingPackage, int callingUid) {
93
        // Always allow calls from the framework, self app or development environment.
94
        if (Process.SYSTEM_UID == callingUid || Process.myUid() == callingUid) {
95
            return true;
96
        }
97
        PackageManager packageManager = context.getPackageManager();
98
        PackageInfo packageInfo;
99
        try {
100
            packageInfo = packageManager.getPackageInfo(
101
                    callingPackage, PackageManager.GET_SIGNATURES);
102
        } catch (PackageManager.NameNotFoundException e) {
103
            LogHelper.w(TAG, e, "Package manager can't find package: ", callingPackage);
104
            return false;
105
        }
106
        if (packageInfo.signatures.length != 1) {
107
            LogHelper.w(TAG, "Caller has more than one signature certificate!");
108
            return false;
109
        }
110
        String signature = Base64.encodeToString(
111
            packageInfo.signatures[0].toByteArray(), Base64.NO_WRAP);
112
 
113
        // Test for known signatures:
114
        ArrayList<CallerInfo> validCallers = mValidCertificates.get(signature);
115
        if (validCallers == null) {
116
            LogHelper.v(TAG, "Signature for caller ", callingPackage, " is not valid: \n"
117
                , signature);
118
            if (mValidCertificates.isEmpty()) {
119
                LogHelper.w(TAG, "The list of valid certificates is empty. Either your file ",
120
                        "res/xml/allowed_media_browser_callers.xml is empty or there was an error ",
121
                        "while reading it. Check previous log messages.");
122
            }
123
            return false;
124
        }
125
 
126
        // Check if the package name is valid for the certificate:
127
        StringBuffer expectedPackages = new StringBuffer();
128
        for (CallerInfo info: validCallers) {
129
            if (callingPackage.equals(info.packageName)) {
130
                LogHelper.v(TAG, "Valid caller: ", info.name, "  package=", info.packageName,
131
                    " release=", info.release);
132
                return true;
133
            }
134
            expectedPackages.append(info.packageName).append(' ');
135
        }
136
 
137
        LogHelper.i(TAG, "Caller has a valid certificate, but its package doesn't match any ",
138
            "expected package for the given certificate. Caller's package is ", callingPackage,
139
            ". Expected packages as defined in res/xml/allowed_media_browser_callers.xml are (",
140
            expectedPackages, "). This caller's certificate is: \n", signature);
141
 
142
        return false;
143
    }
144
 
145
    private final static class CallerInfo {
146
        final String name;
147
        final String packageName;
148
        final boolean release;
149
        final String signingCertificate;
150
 
151
        public CallerInfo(String name, String packageName, boolean release,
152
                          String signingCertificate) {
153
            this.name = name;
154
            this.packageName = packageName;
155
            this.release = release;
156
            this.signingCertificate = signingCertificate;
157
        }
158
    }
159
}
This site uses cookies to store your preferences for site-specific language and display options.

Hooray!

This class requires API level or higher

This doc is hidden because your selected API level for the documentation is . You can change the documentation API level with the selector above the left navigation.

For more information about specifying the API level your app requires, read Supporting Different Platform Versions.

Take a one-minute survey?
Help us improve Android tools and documentation.