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