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.

Get the latest Android developer news and tips that will help you find success on Google Play.

* Required Fields

Hooray!

Browse this site in ?

You requested a page in , but your language preference for this site is .

Would you like to change your language preference and browse this site in ? If you want to change your language preference later, use the language menu at the bottom of each page.