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