1/*
2 * Copyright (C) 2006 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
17package com.android.server.am;
18
19import android.app.IActivityManager.ContentProviderHolder;
20import android.content.ComponentName;
21import android.content.IContentProvider;
22import android.content.pm.ApplicationInfo;
23import android.content.pm.ProviderInfo;
24import android.os.IBinder;
25import android.os.IBinder.DeathRecipient;
26import android.os.Process;
27import android.os.RemoteException;
28import android.os.UserHandle;
29import android.util.Slog;
30
31import java.io.PrintWriter;
32import java.util.ArrayList;
33import java.util.HashMap;
34import java.util.HashSet;
35
36class ContentProviderRecord {
37    final ActivityManagerService service;
38    public final ProviderInfo info;
39    final int uid;
40    final ApplicationInfo appInfo;
41    final ComponentName name;
42    final boolean singleton;
43    public IContentProvider provider;
44    public boolean noReleaseNeeded;
45    // All attached clients
46    final ArrayList<ContentProviderConnection> connections
47            = new ArrayList<ContentProviderConnection>();
48    //final HashSet<ProcessRecord> clients = new HashSet<ProcessRecord>();
49    // Handles for non-framework processes supported by this provider
50    HashMap<IBinder, ExternalProcessHandle> externalProcessTokenToHandle;
51    // Count for external process for which we have no handles.
52    int externalProcessNoHandleCount;
53    ProcessRecord proc; // if non-null, hosting process.
54    ProcessRecord launchingApp; // if non-null, waiting for this app to be launched.
55    String stringName;
56    String shortStringName;
57
58    public ContentProviderRecord(ActivityManagerService _service, ProviderInfo _info,
59            ApplicationInfo ai, ComponentName _name, boolean _singleton) {
60        service = _service;
61        info = _info;
62        uid = ai.uid;
63        appInfo = ai;
64        name = _name;
65        singleton = _singleton;
66        noReleaseNeeded = uid == 0 || uid == Process.SYSTEM_UID;
67    }
68
69    public ContentProviderRecord(ContentProviderRecord cpr) {
70        service = cpr.service;
71        info = cpr.info;
72        uid = cpr.uid;
73        appInfo = cpr.appInfo;
74        name = cpr.name;
75        singleton = cpr.singleton;
76        noReleaseNeeded = cpr.noReleaseNeeded;
77    }
78
79    public ContentProviderHolder newHolder(ContentProviderConnection conn) {
80        ContentProviderHolder holder = new ContentProviderHolder(info);
81        holder.provider = provider;
82        holder.noReleaseNeeded = noReleaseNeeded;
83        holder.connection = conn;
84        return holder;
85    }
86
87    public boolean canRunHere(ProcessRecord app) {
88        return (info.multiprocess || info.processName.equals(app.processName))
89                && uid == app.info.uid;
90    }
91
92    public void addExternalProcessHandleLocked(IBinder token) {
93        if (token == null) {
94            externalProcessNoHandleCount++;
95        } else {
96            if (externalProcessTokenToHandle == null) {
97                externalProcessTokenToHandle = new HashMap<IBinder, ExternalProcessHandle>();
98            }
99            ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
100            if (handle == null) {
101                handle = new ExternalProcessHandle(token);
102                externalProcessTokenToHandle.put(token, handle);
103            }
104            handle.mAcquisitionCount++;
105        }
106    }
107
108    public boolean removeExternalProcessHandleLocked(IBinder token) {
109        if (hasExternalProcessHandles()) {
110            boolean hasHandle = false;
111            if (externalProcessTokenToHandle != null) {
112                ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
113                if (handle != null) {
114                    hasHandle = true;
115                    handle.mAcquisitionCount--;
116                    if (handle.mAcquisitionCount == 0) {
117                        removeExternalProcessHandleInternalLocked(token);
118                        return true;
119                    }
120                }
121            }
122            if (!hasHandle) {
123                externalProcessNoHandleCount--;
124                return true;
125            }
126        }
127        return false;
128    }
129
130    private void removeExternalProcessHandleInternalLocked(IBinder token) {
131        ExternalProcessHandle handle = externalProcessTokenToHandle.get(token);
132        handle.unlinkFromOwnDeathLocked();
133        externalProcessTokenToHandle.remove(token);
134        if (externalProcessTokenToHandle.size() == 0) {
135            externalProcessTokenToHandle = null;
136        }
137    }
138
139    public boolean hasExternalProcessHandles() {
140        return (externalProcessTokenToHandle != null || externalProcessNoHandleCount > 0);
141    }
142
143    void dump(PrintWriter pw, String prefix, boolean full) {
144        if (full) {
145            pw.print(prefix); pw.print("package=");
146                    pw.print(info.applicationInfo.packageName);
147                    pw.print(" process="); pw.println(info.processName);
148        }
149        pw.print(prefix); pw.print("proc="); pw.println(proc);
150        if (launchingApp != null) {
151            pw.print(prefix); pw.print("launchingApp="); pw.println(launchingApp);
152        }
153        if (full) {
154            pw.print(prefix); pw.print("uid="); pw.print(uid);
155                    pw.print(" provider="); pw.println(provider);
156        }
157        if (singleton) {
158            pw.print(prefix); pw.print("singleton="); pw.println(singleton);
159        }
160        pw.print(prefix); pw.print("authority="); pw.println(info.authority);
161        if (full) {
162            if (info.isSyncable || info.multiprocess || info.initOrder != 0) {
163                pw.print(prefix); pw.print("isSyncable="); pw.print(info.isSyncable);
164                        pw.print(" multiprocess="); pw.print(info.multiprocess);
165                        pw.print(" initOrder="); pw.println(info.initOrder);
166            }
167        }
168        if (full) {
169            if (hasExternalProcessHandles()) {
170                pw.print(prefix); pw.print("externals=");
171                        pw.println(externalProcessTokenToHandle.size());
172            }
173        } else {
174            if (connections.size() > 0 || externalProcessNoHandleCount > 0) {
175                pw.print(prefix); pw.print(connections.size());
176                        pw.print(" connections, "); pw.print(externalProcessNoHandleCount);
177                        pw.println(" external handles");
178            }
179        }
180        if (connections.size() > 0) {
181            if (full) {
182                pw.print(prefix); pw.println("Connections:");
183            }
184            for (int i=0; i<connections.size(); i++) {
185                ContentProviderConnection conn = connections.get(i);
186                pw.print(prefix); pw.print("  -> "); pw.println(conn.toClientString());
187                if (conn.provider != this) {
188                    pw.print(prefix); pw.print("    *** WRONG PROVIDER: ");
189                            pw.println(conn.provider);
190                }
191            }
192        }
193    }
194
195    @Override
196    public String toString() {
197        if (stringName != null) {
198            return stringName;
199        }
200        StringBuilder sb = new StringBuilder(128);
201        sb.append("ContentProviderRecord{");
202        sb.append(Integer.toHexString(System.identityHashCode(this)));
203        sb.append(" u");
204        sb.append(UserHandle.getUserId(uid));
205        sb.append(' ');
206        sb.append(name.flattenToShortString());
207        sb.append('}');
208        return stringName = sb.toString();
209    }
210
211    public String toShortString() {
212        if (shortStringName != null) {
213            return shortStringName;
214        }
215        StringBuilder sb = new StringBuilder(128);
216        sb.append(Integer.toHexString(System.identityHashCode(this)));
217        sb.append('/');
218        sb.append(name.flattenToShortString());
219        return shortStringName = sb.toString();
220    }
221
222    // This class represents a handle from an external process to a provider.
223    private class ExternalProcessHandle implements DeathRecipient {
224        private static final String LOG_TAG = "ExternalProcessHanldle";
225
226        private final IBinder mToken;
227        private int mAcquisitionCount;
228
229        public ExternalProcessHandle(IBinder token) {
230            mToken = token;
231            try {
232                token.linkToDeath(this, 0);
233            } catch (RemoteException re) {
234                Slog.e(LOG_TAG, "Couldn't register for death for token: " + mToken, re);
235            }
236        }
237
238        public void unlinkFromOwnDeathLocked() {
239            mToken.unlinkToDeath(this, 0);
240        }
241
242        @Override
243        public void binderDied() {
244            synchronized (service) {
245                if (hasExternalProcessHandles() &&
246                        externalProcessTokenToHandle.get(mToken) != null) {
247                    removeExternalProcessHandleInternalLocked(mToken);
248                }
249            }
250        }
251    }
252}
253