Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5bc68b53 authored by Philip P. Moltmann's avatar Philip P. Moltmann Committed by Android (Google) Code Review
Browse files

Merge "Add a alert that allows the user to approve a print service when we...

Merge "Add a alert that allows the user to approve a print service when we print using it the first time."
parents f49bec04 853a6f56
Loading
Loading
Loading
Loading
+1 −0
Original line number Diff line number Diff line
@@ -46,4 +46,5 @@ oneway interface IPrintSpooler {
    void writePrintJobData(in ParcelFileDescriptor fd, in PrintJobId printJobId);
    void setClient(IPrintSpoolerClient client);
    void setPrintJobCancelling(in PrintJobId printJobId, boolean cancelling);
    void removeApprovedPrintService(in ComponentName serviceToRemove);
}
+12 −0
Original line number Diff line number Diff line
@@ -187,6 +187,18 @@
    <!-- Label for a printer that is not available. [CHAR LIMIT=25] -->
    <string name="printer_unavailable"><xliff:g id="print_job_name" example="Canon-123GHT">%1$s</xliff:g> &#8211; unavailable</string>

    <!-- Title for a warning message about security implications of using a print service,
         displayed as a dialog message when the user prints using a print service that has not been
         used before. [CHAR LIMIT=NONE] -->
    <string name="print_service_security_warning_title">Use
         <xliff:g id="service" example="My Print Service">%1$s</xliff:g>?</string>

    <!-- Summary for a warning message about security implications of using a print service,
         displayed as a dialog message when the user prints using a print service that has not been
         used before. [CHAR LIMIT=NONE] -->
    <string name="print_service_security_warning_summary">Your document may pass through one or
         more servers on its way to the printer.</string>

    <!-- Arrays -->

    <!-- Color mode labels. -->
+19 −3
Original line number Diff line number Diff line
@@ -50,6 +50,7 @@ import com.android.internal.logging.MetricsLogger;
import com.android.internal.os.HandlerCaller;
import com.android.internal.util.FastXmlSerializer;
import com.android.printspooler.R;
import com.android.printspooler.util.ApprovedPrintServices;

import libcore.io.IoUtils;

@@ -67,6 +68,7 @@ import java.io.PrintWriter;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

/**
 * Service for exposing some of the {@link PrintSpooler} functionality to
@@ -136,10 +138,10 @@ public final class PrintSpoolerService extends Service {

    @Override
    protected void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
        synchronized (mLock) {
        String prefix = (args.length > 0) ? args[0] : "";
        String tab = "  ";

        synchronized (mLock) {
            pw.append(prefix).append("print jobs:").println();
            final int printJobCount = mPrintJobs.size();
            for (int i = 0; i < printJobCount; i++) {
@@ -160,6 +162,14 @@ public final class PrintSpoolerService extends Service {
                }
            }
        }

        pw.append(prefix).append("approved print services:").println();
        Set<String> approvedPrintServices = (new ApprovedPrintServices(this)).getApprovedServices();
        if (approvedPrintServices != null) {
            for (String approvedService : approvedPrintServices) {
                pw.append(prefix).append(tab).append(approvedService).println();
            }
        }
    }

    private void sendOnPrintJobQueued(PrintJobInfo printJob) {
@@ -1307,6 +1317,12 @@ public final class PrintSpoolerService extends Service {
            PrintSpoolerService.this.setPrintJobCancelling(printJobId, cancelling);
        }

        @Override
        public void removeApprovedPrintService(ComponentName serviceToRemove) {
            (new ApprovedPrintServices(PrintSpoolerService.this))
                    .removeApprovedService(serviceToRemove);
        }

        public PrintSpoolerService getService() {
            return PrintSpoolerService.this;
        }
+123 −1
Original line number Diff line number Diff line
@@ -17,14 +17,21 @@
package com.android.printspooler.ui;

import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.DialogFragment;
import android.app.Fragment;
import android.app.FragmentTransaction;
import android.content.ActivityNotFoundException;
import android.content.ComponentName;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.ServiceConnection;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.ResolveInfo;
import android.content.res.Configuration;
@@ -81,6 +88,7 @@ import com.android.printspooler.model.RemotePrintDocument;
import com.android.printspooler.model.RemotePrintDocument.RemotePrintDocumentInfo;
import com.android.printspooler.renderer.IPdfEditor;
import com.android.printspooler.renderer.PdfManipulationService;
import com.android.printspooler.util.ApprovedPrintServices;
import com.android.printspooler.util.MediaSizeUtils;
import com.android.printspooler.util.MediaSizeUtils.MediaSizeComparator;
import com.android.printspooler.util.PageRangeUtils;
@@ -88,6 +96,7 @@ import com.android.printspooler.util.PrintOptionUtils;
import com.android.printspooler.widget.PrintContentView;
import com.android.printspooler.widget.PrintContentView.OptionsStateChangeListener;
import com.android.printspooler.widget.PrintContentView.OptionsStateController;

import libcore.io.IoUtils;
import libcore.io.Streams;

@@ -1184,12 +1193,125 @@ public class PrintActivity extends Activity implements RemotePrintDocument.Updat
        mPrintButton.setOnClickListener(clickListener);
    }

    /**
     * A dialog that asks the user to approve a {@link PrintService}. This dialog is automatically
     * dismissed if the same {@link PrintService} gets approved by another
     * {@link PrintServiceApprovalDialog}.
     */
    private static final class PrintServiceApprovalDialog extends DialogFragment
            implements OnSharedPreferenceChangeListener {
        private static final String PRINTSERVICE_KEY = "PRINTSERVICE";
        private ApprovedPrintServices mApprovedServices;

        /**
         * Create a new {@link PrintServiceApprovalDialog} that ask the user to approve a
         * {@link PrintService}.
         *
         * @param printService The {@link ComponentName} of the service to approve
         * @return A new {@link PrintServiceApprovalDialog} that might approve the service
         */
        static PrintServiceApprovalDialog newInstance(ComponentName printService) {
            PrintServiceApprovalDialog dialog = new PrintServiceApprovalDialog();

            Bundle args = new Bundle();
            args.putParcelable(PRINTSERVICE_KEY, printService);
            dialog.setArguments(args);

            return dialog;
        }

        @Override
        public void onStop() {
            super.onStop();

            mApprovedServices.unregisterChangeListener(this);
        }

        @Override
        public void onStart() {
            super.onStart();

            ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);
            synchronized (ApprovedPrintServices.sLock) {
                if (mApprovedServices.isApprovedService(printService)) {
                    dismiss();
                } else {
                    mApprovedServices.registerChangeListenerLocked(this);
                }
            }
        }

        @Override
        public Dialog onCreateDialog(Bundle savedInstanceState) {
            super.onCreateDialog(savedInstanceState);

            mApprovedServices = new ApprovedPrintServices(getActivity());

            PackageManager packageManager = getActivity().getPackageManager();
            CharSequence serviceLabel;
            try {
                ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);

                serviceLabel = packageManager.getApplicationInfo(printService.getPackageName(), 0)
                        .loadLabel(packageManager);
            } catch (NameNotFoundException e) {
                serviceLabel = null;
            }

            AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
            builder.setTitle(getString(R.string.print_service_security_warning_title,
                    serviceLabel))
                    .setMessage(getString(R.string.print_service_security_warning_summary,
                            serviceLabel))
                    .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int id) {
                            ComponentName printService =
                                    getArguments().getParcelable(PRINTSERVICE_KEY);
                            // Prevent onSharedPreferenceChanged from getting triggered
                            mApprovedServices
                                    .unregisterChangeListener(PrintServiceApprovalDialog.this);

                            mApprovedServices.addApprovedService(printService);
                            ((PrintActivity) getActivity()).confirmPrint();
                        }
                    })
                    .setNegativeButton(android.R.string.cancel, null);

            return builder.create();
        }

        @Override
        public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
            ComponentName printService = getArguments().getParcelable(PRINTSERVICE_KEY);

            synchronized (ApprovedPrintServices.sLock) {
                if (mApprovedServices.isApprovedService(printService)) {
                    dismiss();
                }
            }
        }
    }

    private final class MyClickListener implements OnClickListener {
        @Override
        public void onClick(View view) {
            if (view == mPrintButton) {
                if (mCurrentPrinter != null) {
                    if (mDestinationSpinnerAdapter.getPdfPrinter() == mCurrentPrinter) {
                        confirmPrint();
                    } else {
                        ApprovedPrintServices approvedServices =
                                new ApprovedPrintServices(PrintActivity.this);

                        ComponentName printService = mCurrentPrinter.getId().getServiceName();
                        if (approvedServices.isApprovedService(printService)) {
                            confirmPrint();
                        } else {
                            PrintServiceApprovalDialog.newInstance(printService)
                                    .show(getFragmentManager(), "approve");
                        }
                    }
                } else {
                    cancelPrint();
                }
+156 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2015 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.printspooler.util;

import android.content.ComponentName;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.printservice.PrintService;
import android.util.ArraySet;

import java.util.Set;

/**
 * Manage approved print services. These services are stored in the shared preferences.
 */
public class ApprovedPrintServices {
    /**
     * Used for locking accesses to the approved services.
     */
    static final public Object sLock = new Object();

    private static final String APPROVED_SERVICES_PREFERENCE = "PRINT_SPOOLER_APPROVED_SERVICES";
    private final SharedPreferences mPreferences;

    /**
     * Create a new {@link ApprovedPrintServices}
     *
     * @param owner The {@link Context} using this object.
     */
    public ApprovedPrintServices(Context owner) {
        mPreferences = owner.getSharedPreferences(APPROVED_SERVICES_PREFERENCE,
                Context.MODE_PRIVATE);
    }

    /**
     * Get {@link Set} of approved services.
     *
     * @return A {@link Set} containing all currently approved services.
     */
    public Set<String> getApprovedServices() {
        return mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);
    }

    /**
     * Check if a {@link PrintService} is approved.
     *
     * This function does not acquire the {@link #sLock}.
     *
     * @param service The {@link ComponentName} of the {@link PrintService} that might be approved
     * @return true iff the service is currently approved
     */
    public boolean isApprovedService(ComponentName service) {
        final Set<String> approvedServices = getApprovedServices();

        if (approvedServices != null) {
            final String flattenedString = service.flattenToShortString();

            for (String approvedService : approvedServices) {
                if (approvedService.equals(flattenedString)) {
                    return true;
                }
            }
        }

        return false;
    }

    /**
     * Add a {@link PrintService} to the list of approved print services.
     *
     * @param serviceToAdd The {@link ComponentName} of the {@link PrintService} to be approved.
     */
    public void addApprovedService(ComponentName serviceToAdd) {
        synchronized (sLock) {
            Set<String> oldApprovedServices =
                    mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null);

            Set<String> newApprovedServices;
            if (oldApprovedServices == null) {
                newApprovedServices = new ArraySet<String>(1);
            } else {
                // Copy approved services.
                newApprovedServices = new ArraySet<String>(oldApprovedServices);
            }
            newApprovedServices.add(serviceToAdd.flattenToShortString());

            SharedPreferences.Editor editor = mPreferences.edit();
            editor.putStringSet(APPROVED_SERVICES_PREFERENCE, newApprovedServices);
            editor.apply();
        }
    }

    /**
     * Add a {@link OnSharedPreferenceChangeListener} that listens for changes to the approved
     * services. Should only be called while holding {@link #sLock} to synchronize against
     * {@link #addApprovedService}.
     *
     * @param listener {@link OnSharedPreferenceChangeListener} to register
     */
    public void registerChangeListenerLocked(OnSharedPreferenceChangeListener listener) {
        mPreferences.registerOnSharedPreferenceChangeListener(listener);
    }

    /**
     * Unregister a listener registered in {@link #registerChangeListenerLocked}.
     *
     * @param listener {@link OnSharedPreferenceChangeListener} to unregister
     */
    public void unregisterChangeListener(OnSharedPreferenceChangeListener listener) {
        mPreferences.unregisterOnSharedPreferenceChangeListener(listener);
    }

    /**
     * If a {@link PrintService} is approved, remove it from the list of approved services.
     *
     * @param serviceToRemove The {@link ComponentName} of the {@link PrintService} to be removed
     */
    public void removeApprovedService(ComponentName serviceToRemove) {
        synchronized (sLock) {
            if (isApprovedService(serviceToRemove)) {
                // Copy approved services.
                ArraySet<String> approvedServices = new ArraySet<String>(
                        mPreferences.getStringSet(APPROVED_SERVICES_PREFERENCE, null));

                SharedPreferences.Editor editor = mPreferences.edit();

                final int numApprovedServices = approvedServices.size();
                for (int i = 0; i < numApprovedServices; i++) {
                    if (approvedServices.valueAt(i)
                            .equals(serviceToRemove.flattenToShortString())) {
                        approvedServices.removeAt(i);
                        break;
                    }
                }

                editor.putStringSet(APPROVED_SERVICES_PREFERENCE, approvedServices);
                editor.apply();
            }
        }
    }
}
Loading