1 /*
2  * Copyright (C) 2016 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 
17 package com.android.printservice.recommendation;
18 
19 import android.annotation.IntRange;
20 import android.annotation.NonNull;
21 import android.annotation.StringRes;
22 import com.android.internal.util.Preconditions;
23 
24 /**
25  * Wrapper for a {@link PrintServicePlugin}, isolating issues with the plugin as good as possible
26  * from the {@link RecommendationServiceImpl service}.
27  */
28 class RemotePrintServicePlugin implements PrintServicePlugin.PrinterDiscoveryCallback {
29     /** Lock for this object */
30     private final Object mLock = new Object();
31 
32     /** The name of the print service. */
33     public final @StringRes int name;
34 
35     /** If the print service if for more than a single vendor */
36     public final boolean recommendsMultiVendorService;
37 
38     /** The package name of the full print service */
39     public final @NonNull CharSequence packageName;
40 
41     /** Wrapped plugin */
42     private final @NonNull PrintServicePlugin mPlugin;
43 
44     /** The number of printers discovered by the plugin */
45     private @IntRange(from = 0) int mNumPrinters;
46 
47     /** If the plugin is started by not yet stopped */
48     private boolean isRunning;
49 
50     /** Listener for changes to {@link #mNumPrinters}. */
51     private @NonNull OnChangedListener mListener;
52 
53     /**
54      * Create a new remote for a {@link PrintServicePlugin plugin}.
55      *
56      * @param plugin                       The plugin to be wrapped
57      * @param listener                     The listener to be notified about changes in this plugin
58      * @param recommendsMultiVendorService If the plugin detects printers of more than a single
59      *                                     vendor
60      *
61      * @throws PluginException If the plugin has issues while caching basic stub properties
62      */
RemotePrintServicePlugin(@onNull PrintServicePlugin plugin, @NonNull OnChangedListener listener, boolean recommendsMultiVendorService)63     public RemotePrintServicePlugin(@NonNull PrintServicePlugin plugin,
64             @NonNull OnChangedListener listener, boolean recommendsMultiVendorService)
65             throws PluginException {
66         mListener = listener;
67         mPlugin = plugin;
68         this.recommendsMultiVendorService = recommendsMultiVendorService;
69 
70         // We handle any throwable to isolate our self from bugs in the plugin code.
71         // Cache simple properties to avoid having to deal with exceptions later in the code.
72         try {
73             name = Preconditions.checkArgumentPositive(mPlugin.getName(), "name");
74             packageName = Preconditions.checkStringNotEmpty(mPlugin.getPackageName(),
75                     "packageName");
76         } catch (Throwable e) {
77             throw new PluginException(mPlugin, "Cannot cache simple properties ", e);
78         }
79 
80         isRunning = false;
81     }
82 
83     /**
84      * Start the plugin. From now on there might be callbacks to the registered listener.
85      */
start()86     public void start()
87             throws PluginException {
88         // We handle any throwable to isolate our self from bugs in the stub code
89         try {
90             synchronized (mLock) {
91                 isRunning = true;
92                 mPlugin.start(this);
93             }
94         } catch (Throwable e) {
95             throw new PluginException(mPlugin, "Cannot start", e);
96         }
97     }
98 
99     /**
100      * Stop the plugin. From this call on there will not be any more callbacks.
101      */
stop()102     public void stop() throws PluginException {
103         // We handle any throwable to isolate our self from bugs in the stub code
104         try {
105             synchronized (mLock) {
106                 mPlugin.stop();
107                 isRunning = false;
108             }
109         } catch (Throwable e) {
110             throw new PluginException(mPlugin, "Cannot stop", e);
111         }
112     }
113 
114     /**
115      * Get the current number of printers reported by the stub.
116      *
117      * @return The number of printers reported by the stub.
118      */
getNumPrinters()119     public @IntRange(from = 0) int getNumPrinters() {
120         return mNumPrinters;
121     }
122 
123     @Override
onChanged(@ntRangefrom = 0) int numDiscoveredPrinters)124     public void onChanged(@IntRange(from = 0) int numDiscoveredPrinters) {
125         synchronized (mLock) {
126             Preconditions.checkState(isRunning);
127 
128             mNumPrinters = Preconditions.checkArgumentNonnegative(numDiscoveredPrinters,
129                     "numDiscoveredPrinters");
130 
131             if (mNumPrinters > 0) {
132                 mListener.onChanged();
133             }
134         }
135     }
136 
137     /**
138      * Listener to listen for changes to {@link #getNumPrinters}
139      */
140     public interface OnChangedListener {
onChanged()141         void onChanged();
142     }
143 
144     /**
145      * Exception thrown if the stub has any issues.
146      */
147     public class PluginException extends Exception {
PluginException(PrintServicePlugin plugin, String message, Throwable e)148         private PluginException(PrintServicePlugin plugin, String message, Throwable e) {
149             super(plugin + ": " + message, e);
150         }
151     }
152 }
153