1 /*
2  * Copyright (C) 2023 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 package com.android.systemui.mediaprojection.devicepolicy
17 
18 import android.app.admin.DevicePolicyManager
19 import android.os.UserHandle
20 import android.os.UserManager
21 import androidx.test.filters.SmallTest
22 import com.android.systemui.SysuiTestCase
23 import com.android.systemui.util.mockito.eq
24 import com.android.systemui.util.mockito.mock
25 import com.android.systemui.util.mockito.whenever
26 import com.google.common.truth.Truth.assertWithMessage
27 import org.junit.Before
28 import org.junit.Test
29 import org.junit.runner.RunWith
30 import org.junit.runners.Parameterized
31 import org.junit.runners.Parameterized.Parameters
32 import org.mockito.ArgumentMatchers.any
33 
34 abstract class BaseScreenCaptureDevicePolicyResolverTest(private val precondition: Preconditions) :
35     SysuiTestCase() {
36 
37     abstract class Preconditions(
38         val personalScreenCaptureDisabled: Boolean,
39         val workScreenCaptureDisabled: Boolean,
40         val disallowShareIntoManagedProfile: Boolean
41     )
42 
43     protected val devicePolicyManager: DevicePolicyManager = mock()
44     protected val userManager: UserManager = mock()
45 
46     protected val personalUserHandle: UserHandle = UserHandle.of(123)
47     protected val workUserHandle: UserHandle = UserHandle.of(456)
48 
49     protected val policyResolver =
50         ScreenCaptureDevicePolicyResolver(
51             devicePolicyManager,
52             userManager,
53             personalUserHandle,
54             workUserHandle
55         )
56 
57     @Before
58     fun setUp() {
59         setUpPolicies()
60     }
61 
62     private fun setUpPolicies() {
63         whenever(
64                 devicePolicyManager.getScreenCaptureDisabled(
65                     any(),
66                     eq(personalUserHandle.identifier)
67                 )
68             )
69             .thenReturn(precondition.personalScreenCaptureDisabled)
70 
71         whenever(devicePolicyManager.getScreenCaptureDisabled(any(), eq(workUserHandle.identifier)))
72             .thenReturn(precondition.workScreenCaptureDisabled)
73 
74         whenever(
75                 userManager.hasUserRestrictionForUser(
76                     eq(UserManager.DISALLOW_SHARE_INTO_MANAGED_PROFILE),
77                     eq(workUserHandle)
78                 )
79             )
80             .thenReturn(precondition.disallowShareIntoManagedProfile)
81     }
82 }
83 
84 @RunWith(Parameterized::class)
85 @SmallTest
86 class IsAllowedScreenCaptureDevicePolicyResolverTest(
87     private val test: IsScreenCaptureAllowedTestCase
88 ) : BaseScreenCaptureDevicePolicyResolverTest(test.given) {
89 
90     companion object {
91         @Parameters(name = "{0}")
92         @JvmStatic
93         fun getParams() =
94             listOf(
95                 IsScreenCaptureAllowedTestCase(
96                     given =
97                         Preconditions(
98                             isHostInWorkProfile = false,
99                             isTargetInWorkProfile = false,
100                             personalScreenCaptureDisabled = false,
101                             workScreenCaptureDisabled = false,
102                             disallowShareIntoManagedProfile = false,
103                         ),
104                     expectedScreenCaptureAllowed = true,
105                 ),
106                 IsScreenCaptureAllowedTestCase(
107                     given =
108                         Preconditions(
109                             isHostInWorkProfile = false,
110                             isTargetInWorkProfile = false,
111                             personalScreenCaptureDisabled = false,
112                             workScreenCaptureDisabled = false,
113                             disallowShareIntoManagedProfile = true,
114                         ),
115                     expectedScreenCaptureAllowed = true,
116                 ),
117                 IsScreenCaptureAllowedTestCase(
118                     given =
119                         Preconditions(
120                             isHostInWorkProfile = false,
121                             isTargetInWorkProfile = false,
122                             personalScreenCaptureDisabled = false,
123                             workScreenCaptureDisabled = true,
124                             disallowShareIntoManagedProfile = false,
125                         ),
126                     expectedScreenCaptureAllowed = true,
127                 ),
128                 IsScreenCaptureAllowedTestCase(
129                     given =
130                         Preconditions(
131                             isHostInWorkProfile = false,
132                             isTargetInWorkProfile = false,
133                             personalScreenCaptureDisabled = false,
134                             workScreenCaptureDisabled = true,
135                             disallowShareIntoManagedProfile = true,
136                         ),
137                     expectedScreenCaptureAllowed = true,
138                 ),
139                 IsScreenCaptureAllowedTestCase(
140                     given =
141                         Preconditions(
142                             isHostInWorkProfile = false,
143                             isTargetInWorkProfile = false,
144                             personalScreenCaptureDisabled = true,
145                             workScreenCaptureDisabled = false,
146                             disallowShareIntoManagedProfile = false
147                         ),
148                     expectedScreenCaptureAllowed = false,
149                 ),
150                 IsScreenCaptureAllowedTestCase(
151                     given =
152                         Preconditions(
153                             isHostInWorkProfile = false,
154                             isTargetInWorkProfile = false,
155                             personalScreenCaptureDisabled = true,
156                             workScreenCaptureDisabled = false,
157                             disallowShareIntoManagedProfile = true
158                         ),
159                     expectedScreenCaptureAllowed = false,
160                 ),
161                 IsScreenCaptureAllowedTestCase(
162                     given =
163                         Preconditions(
164                             isHostInWorkProfile = false,
165                             isTargetInWorkProfile = false,
166                             personalScreenCaptureDisabled = true,
167                             workScreenCaptureDisabled = true,
168                             disallowShareIntoManagedProfile = false
169                         ),
170                     expectedScreenCaptureAllowed = false,
171                 ),
172                 IsScreenCaptureAllowedTestCase(
173                     given =
174                         Preconditions(
175                             isHostInWorkProfile = false,
176                             isTargetInWorkProfile = false,
177                             personalScreenCaptureDisabled = true,
178                             workScreenCaptureDisabled = true,
179                             disallowShareIntoManagedProfile = true
180                         ),
181                     expectedScreenCaptureAllowed = false,
182                 ),
183                 IsScreenCaptureAllowedTestCase(
184                     given =
185                         Preconditions(
186                             isHostInWorkProfile = false,
187                             isTargetInWorkProfile = true,
188                             personalScreenCaptureDisabled = false,
189                             workScreenCaptureDisabled = false,
190                             disallowShareIntoManagedProfile = false
191                         ),
192                     expectedScreenCaptureAllowed = true,
193                 ),
194                 IsScreenCaptureAllowedTestCase(
195                     given =
196                         Preconditions(
197                             isHostInWorkProfile = false,
198                             isTargetInWorkProfile = true,
199                             personalScreenCaptureDisabled = false,
200                             workScreenCaptureDisabled = false,
201                             disallowShareIntoManagedProfile = true
202                         ),
203                     expectedScreenCaptureAllowed = true,
204                 ),
205                 IsScreenCaptureAllowedTestCase(
206                     given =
207                         Preconditions(
208                             isHostInWorkProfile = false,
209                             isTargetInWorkProfile = true,
210                             personalScreenCaptureDisabled = false,
211                             workScreenCaptureDisabled = true,
212                             disallowShareIntoManagedProfile = false
213                         ),
214                     expectedScreenCaptureAllowed = false,
215                 ),
216                 IsScreenCaptureAllowedTestCase(
217                     given =
218                         Preconditions(
219                             isHostInWorkProfile = false,
220                             isTargetInWorkProfile = true,
221                             personalScreenCaptureDisabled = false,
222                             workScreenCaptureDisabled = true,
223                             disallowShareIntoManagedProfile = true
224                         ),
225                     expectedScreenCaptureAllowed = false,
226                 ),
227                 IsScreenCaptureAllowedTestCase(
228                     given =
229                         Preconditions(
230                             isHostInWorkProfile = false,
231                             isTargetInWorkProfile = true,
232                             personalScreenCaptureDisabled = true,
233                             workScreenCaptureDisabled = false,
234                             disallowShareIntoManagedProfile = false
235                         ),
236                     expectedScreenCaptureAllowed = false,
237                 ),
238                 IsScreenCaptureAllowedTestCase(
239                     given =
240                         Preconditions(
241                             isHostInWorkProfile = false,
242                             isTargetInWorkProfile = true,
243                             personalScreenCaptureDisabled = true,
244                             workScreenCaptureDisabled = false,
245                             disallowShareIntoManagedProfile = true
246                         ),
247                     expectedScreenCaptureAllowed = false,
248                 ),
249                 IsScreenCaptureAllowedTestCase(
250                     given =
251                         Preconditions(
252                             isHostInWorkProfile = false,
253                             isTargetInWorkProfile = true,
254                             personalScreenCaptureDisabled = true,
255                             workScreenCaptureDisabled = true,
256                             disallowShareIntoManagedProfile = false
257                         ),
258                     expectedScreenCaptureAllowed = false,
259                 ),
260                 IsScreenCaptureAllowedTestCase(
261                     given =
262                         Preconditions(
263                             isHostInWorkProfile = false,
264                             isTargetInWorkProfile = true,
265                             personalScreenCaptureDisabled = true,
266                             workScreenCaptureDisabled = true,
267                             disallowShareIntoManagedProfile = true
268                         ),
269                     expectedScreenCaptureAllowed = false,
270                 ),
271                 IsScreenCaptureAllowedTestCase(
272                     given =
273                         Preconditions(
274                             isHostInWorkProfile = true,
275                             isTargetInWorkProfile = false,
276                             personalScreenCaptureDisabled = false,
277                             workScreenCaptureDisabled = false,
278                             disallowShareIntoManagedProfile = false
279                         ),
280                     expectedScreenCaptureAllowed = true,
281                 ),
282                 IsScreenCaptureAllowedTestCase(
283                     given =
284                         Preconditions(
285                             isHostInWorkProfile = true,
286                             isTargetInWorkProfile = false,
287                             personalScreenCaptureDisabled = false,
288                             workScreenCaptureDisabled = false,
289                             disallowShareIntoManagedProfile = true
290                         ),
291                     expectedScreenCaptureAllowed = false,
292                 ),
293                 IsScreenCaptureAllowedTestCase(
294                     given =
295                         Preconditions(
296                             isHostInWorkProfile = true,
297                             isTargetInWorkProfile = false,
298                             personalScreenCaptureDisabled = false,
299                             workScreenCaptureDisabled = true,
300                             disallowShareIntoManagedProfile = false
301                         ),
302                     expectedScreenCaptureAllowed = false,
303                 ),
304                 IsScreenCaptureAllowedTestCase(
305                     given =
306                         Preconditions(
307                             isHostInWorkProfile = true,
308                             isTargetInWorkProfile = false,
309                             personalScreenCaptureDisabled = false,
310                             workScreenCaptureDisabled = true,
311                             disallowShareIntoManagedProfile = true
312                         ),
313                     expectedScreenCaptureAllowed = false,
314                 ),
315                 IsScreenCaptureAllowedTestCase(
316                     given =
317                         Preconditions(
318                             isHostInWorkProfile = true,
319                             isTargetInWorkProfile = false,
320                             personalScreenCaptureDisabled = true,
321                             workScreenCaptureDisabled = false,
322                             disallowShareIntoManagedProfile = false
323                         ),
324                     expectedScreenCaptureAllowed = false,
325                 ),
326                 IsScreenCaptureAllowedTestCase(
327                     given =
328                         Preconditions(
329                             isHostInWorkProfile = true,
330                             isTargetInWorkProfile = false,
331                             personalScreenCaptureDisabled = true,
332                             workScreenCaptureDisabled = false,
333                             disallowShareIntoManagedProfile = true
334                         ),
335                     expectedScreenCaptureAllowed = false,
336                 ),
337                 IsScreenCaptureAllowedTestCase(
338                     given =
339                         Preconditions(
340                             isHostInWorkProfile = true,
341                             isTargetInWorkProfile = false,
342                             personalScreenCaptureDisabled = true,
343                             workScreenCaptureDisabled = true,
344                             disallowShareIntoManagedProfile = false
345                         ),
346                     expectedScreenCaptureAllowed = false,
347                 ),
348                 IsScreenCaptureAllowedTestCase(
349                     given =
350                         Preconditions(
351                             isHostInWorkProfile = true,
352                             isTargetInWorkProfile = false,
353                             personalScreenCaptureDisabled = true,
354                             workScreenCaptureDisabled = true,
355                             disallowShareIntoManagedProfile = true
356                         ),
357                     expectedScreenCaptureAllowed = false,
358                 ),
359                 IsScreenCaptureAllowedTestCase(
360                     given =
361                         Preconditions(
362                             isHostInWorkProfile = true,
363                             isTargetInWorkProfile = true,
364                             personalScreenCaptureDisabled = false,
365                             workScreenCaptureDisabled = false,
366                             disallowShareIntoManagedProfile = false
367                         ),
368                     expectedScreenCaptureAllowed = true,
369                 ),
370                 IsScreenCaptureAllowedTestCase(
371                     given =
372                         Preconditions(
373                             isHostInWorkProfile = true,
374                             isTargetInWorkProfile = true,
375                             personalScreenCaptureDisabled = false,
376                             workScreenCaptureDisabled = false,
377                             disallowShareIntoManagedProfile = true
378                         ),
379                     expectedScreenCaptureAllowed = true,
380                 ),
381                 IsScreenCaptureAllowedTestCase(
382                     given =
383                         Preconditions(
384                             isHostInWorkProfile = true,
385                             isTargetInWorkProfile = true,
386                             personalScreenCaptureDisabled = false,
387                             workScreenCaptureDisabled = true,
388                             disallowShareIntoManagedProfile = false
389                         ),
390                     expectedScreenCaptureAllowed = false,
391                 ),
392                 IsScreenCaptureAllowedTestCase(
393                     given =
394                         Preconditions(
395                             isHostInWorkProfile = true,
396                             isTargetInWorkProfile = true,
397                             personalScreenCaptureDisabled = false,
398                             workScreenCaptureDisabled = true,
399                             disallowShareIntoManagedProfile = true
400                         ),
401                     expectedScreenCaptureAllowed = false,
402                 ),
403                 IsScreenCaptureAllowedTestCase(
404                     given =
405                         Preconditions(
406                             isHostInWorkProfile = true,
407                             isTargetInWorkProfile = true,
408                             personalScreenCaptureDisabled = true,
409                             workScreenCaptureDisabled = true,
410                             disallowShareIntoManagedProfile = false
411                         ),
412                     expectedScreenCaptureAllowed = false,
413                 ),
414                 IsScreenCaptureAllowedTestCase(
415                     given =
416                         Preconditions(
417                             isHostInWorkProfile = true,
418                             isTargetInWorkProfile = true,
419                             personalScreenCaptureDisabled = true,
420                             workScreenCaptureDisabled = true,
421                             disallowShareIntoManagedProfile = true
422                         ),
423                     expectedScreenCaptureAllowed = false,
424                 ),
425             )
426     }
427 
428     class Preconditions(
429         personalScreenCaptureDisabled: Boolean,
430         workScreenCaptureDisabled: Boolean,
431         disallowShareIntoManagedProfile: Boolean,
432         val isHostInWorkProfile: Boolean,
433         val isTargetInWorkProfile: Boolean,
434     ) :
435         BaseScreenCaptureDevicePolicyResolverTest.Preconditions(
436             personalScreenCaptureDisabled,
437             workScreenCaptureDisabled,
438             disallowShareIntoManagedProfile
439         )
440 
441     data class IsScreenCaptureAllowedTestCase(
442         val given: Preconditions,
443         val expectedScreenCaptureAllowed: Boolean
444     ) {
445         override fun toString(): String =
446             "isScreenCaptureAllowed: " +
447                 "host[${if (given.isHostInWorkProfile) "work" else "personal"} profile], " +
448                 "target[${if (given.isTargetInWorkProfile) "work" else "personal"} profile], " +
449                 "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " +
450                 "work screen capture disabled = ${given.workScreenCaptureDisabled}, " +
451                 "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " +
452                 "expected screen capture allowed = $expectedScreenCaptureAllowed"
453     }
454 
455     @Test
456     fun test() {
457         val targetAppUserHandle =
458             if (test.given.isTargetInWorkProfile) workUserHandle else personalUserHandle
459         val hostAppUserHandle =
460             if (test.given.isHostInWorkProfile) workUserHandle else personalUserHandle
461 
462         val screenCaptureAllowed =
463             policyResolver.isScreenCaptureAllowed(targetAppUserHandle, hostAppUserHandle)
464 
465         assertWithMessage("Screen capture policy resolved incorrectly")
466             .that(screenCaptureAllowed)
467             .isEqualTo(test.expectedScreenCaptureAllowed)
468     }
469 }
470 
471 @RunWith(Parameterized::class)
472 @SmallTest
473 class IsCompletelyNotAllowedScreenCaptureDevicePolicyResolverTest(
474     private val test: IsScreenCaptureCompletelyDisabledTestCase
475 ) : BaseScreenCaptureDevicePolicyResolverTest(test.given) {
476 
477     companion object {
478         @Parameters(name = "{0}")
479         @JvmStatic
480         fun getParams() =
481             listOf(
482                 IsScreenCaptureCompletelyDisabledTestCase(
483                     given =
484                         Preconditions(
485                             isHostInWorkProfile = false,
486                             personalScreenCaptureDisabled = false,
487                             workScreenCaptureDisabled = false,
488                             disallowShareIntoManagedProfile = false
489                         ),
490                     expectedScreenCaptureCompletelyDisabled = false,
491                 ),
492                 IsScreenCaptureCompletelyDisabledTestCase(
493                     given =
494                         Preconditions(
495                             isHostInWorkProfile = false,
496                             personalScreenCaptureDisabled = false,
497                             workScreenCaptureDisabled = false,
498                             disallowShareIntoManagedProfile = true
499                         ),
500                     expectedScreenCaptureCompletelyDisabled = false,
501                 ),
502                 IsScreenCaptureCompletelyDisabledTestCase(
503                     given =
504                         Preconditions(
505                             isHostInWorkProfile = false,
506                             personalScreenCaptureDisabled = false,
507                             workScreenCaptureDisabled = true,
508                             disallowShareIntoManagedProfile = false
509                         ),
510                     expectedScreenCaptureCompletelyDisabled = false,
511                 ),
512                 IsScreenCaptureCompletelyDisabledTestCase(
513                     given =
514                         Preconditions(
515                             isHostInWorkProfile = false,
516                             personalScreenCaptureDisabled = false,
517                             workScreenCaptureDisabled = true,
518                             disallowShareIntoManagedProfile = true
519                         ),
520                     expectedScreenCaptureCompletelyDisabled = false,
521                 ),
522                 IsScreenCaptureCompletelyDisabledTestCase(
523                     given =
524                         Preconditions(
525                             isHostInWorkProfile = false,
526                             personalScreenCaptureDisabled = true,
527                             workScreenCaptureDisabled = false,
528                             disallowShareIntoManagedProfile = false
529                         ),
530                     expectedScreenCaptureCompletelyDisabled = true,
531                 ),
532                 IsScreenCaptureCompletelyDisabledTestCase(
533                     given =
534                         Preconditions(
535                             isHostInWorkProfile = false,
536                             personalScreenCaptureDisabled = true,
537                             workScreenCaptureDisabled = false,
538                             disallowShareIntoManagedProfile = true
539                         ),
540                     expectedScreenCaptureCompletelyDisabled = true,
541                 ),
542                 IsScreenCaptureCompletelyDisabledTestCase(
543                     given =
544                         Preconditions(
545                             isHostInWorkProfile = false,
546                             personalScreenCaptureDisabled = true,
547                             workScreenCaptureDisabled = true,
548                             disallowShareIntoManagedProfile = false
549                         ),
550                     expectedScreenCaptureCompletelyDisabled = true,
551                 ),
552                 IsScreenCaptureCompletelyDisabledTestCase(
553                     given =
554                         Preconditions(
555                             isHostInWorkProfile = false,
556                             personalScreenCaptureDisabled = true,
557                             workScreenCaptureDisabled = true,
558                             disallowShareIntoManagedProfile = true
559                         ),
560                     expectedScreenCaptureCompletelyDisabled = true,
561                 ),
562                 IsScreenCaptureCompletelyDisabledTestCase(
563                     given =
564                         Preconditions(
565                             isHostInWorkProfile = true,
566                             personalScreenCaptureDisabled = false,
567                             workScreenCaptureDisabled = false,
568                             disallowShareIntoManagedProfile = false
569                         ),
570                     expectedScreenCaptureCompletelyDisabled = false,
571                 ),
572                 IsScreenCaptureCompletelyDisabledTestCase(
573                     given =
574                         Preconditions(
575                             isHostInWorkProfile = true,
576                             personalScreenCaptureDisabled = false,
577                             workScreenCaptureDisabled = false,
578                             disallowShareIntoManagedProfile = true
579                         ),
580                     expectedScreenCaptureCompletelyDisabled = false,
581                 ),
582                 IsScreenCaptureCompletelyDisabledTestCase(
583                     given =
584                         Preconditions(
585                             isHostInWorkProfile = true,
586                             personalScreenCaptureDisabled = false,
587                             workScreenCaptureDisabled = true,
588                             disallowShareIntoManagedProfile = false
589                         ),
590                     expectedScreenCaptureCompletelyDisabled = true,
591                 ),
592                 IsScreenCaptureCompletelyDisabledTestCase(
593                     given =
594                         Preconditions(
595                             isHostInWorkProfile = true,
596                             personalScreenCaptureDisabled = false,
597                             workScreenCaptureDisabled = true,
598                             disallowShareIntoManagedProfile = true
599                         ),
600                     expectedScreenCaptureCompletelyDisabled = true,
601                 ),
602                 IsScreenCaptureCompletelyDisabledTestCase(
603                     given =
604                         Preconditions(
605                             isHostInWorkProfile = true,
606                             personalScreenCaptureDisabled = true,
607                             workScreenCaptureDisabled = true,
608                             disallowShareIntoManagedProfile = false
609                         ),
610                     expectedScreenCaptureCompletelyDisabled = true,
611                 ),
612                 IsScreenCaptureCompletelyDisabledTestCase(
613                     given =
614                         Preconditions(
615                             isHostInWorkProfile = true,
616                             personalScreenCaptureDisabled = true,
617                             workScreenCaptureDisabled = true,
618                             disallowShareIntoManagedProfile = true
619                         ),
620                     expectedScreenCaptureCompletelyDisabled = true,
621                 )
622             )
623     }
624 
625     class Preconditions(
626         personalScreenCaptureDisabled: Boolean,
627         workScreenCaptureDisabled: Boolean,
628         disallowShareIntoManagedProfile: Boolean,
629         val isHostInWorkProfile: Boolean,
630     ) :
631         BaseScreenCaptureDevicePolicyResolverTest.Preconditions(
632             personalScreenCaptureDisabled,
633             workScreenCaptureDisabled,
634             disallowShareIntoManagedProfile
635         )
636 
637     data class IsScreenCaptureCompletelyDisabledTestCase(
638         val given: Preconditions,
639         val expectedScreenCaptureCompletelyDisabled: Boolean
640     ) {
641         override fun toString(): String =
642             "isScreenCaptureCompletelyDisabled: " +
643                 "host[${if (given.isHostInWorkProfile) "work" else "personal"} profile], " +
644                 "personal screen capture disabled = ${given.personalScreenCaptureDisabled}, " +
645                 "work screen capture disabled = ${given.workScreenCaptureDisabled}, " +
646                 "disallow share into managed profile = ${given.disallowShareIntoManagedProfile}, " +
647                 "expected screen capture completely disabled = " +
648                 "$expectedScreenCaptureCompletelyDisabled"
649     }
650 
651     @Test
652     fun test() {
653         val hostAppUserHandle =
654             if (test.given.isHostInWorkProfile) workUserHandle else personalUserHandle
655 
656         val completelyDisabled = policyResolver.isScreenCaptureCompletelyDisabled(hostAppUserHandle)
657 
658         assertWithMessage("Screen capture policy resolved incorrectly")
659             .that(completelyDisabled)
660             .isEqualTo(test.expectedScreenCaptureCompletelyDisabled)
661     }
662 }
663